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", no_symlinks ? temp : 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. The check for cdpath[0]
280 is so we don't mistakenly treat a CDPATH value of "" as not
281 specifying the current directory. */
282 if (posixly_correct && cdpath[0])
284 builtin_error ("%s: %s", dirname, strerror (ENOENT));
285 return (EXECUTION_FAILURE);
289 if (change_to_directory (dirname, no_symlinks))
290 return (bindpwd (no_symlinks));
292 /* If the user requests it, then perhaps this is the name of
293 a shell variable, whose value contains the directory to
294 change to. If that is the case, then change to that
298 temp = get_string_value (dirname);
299 if (temp && change_to_directory (temp, no_symlinks))
301 printf ("%s\n", temp);
302 return (bindpwd (no_symlinks));
306 /* If the user requests it, try to find a directory name similar in
307 spelling to the one requested, in case the user made a simple
308 typo. This is similar to the UNIX 8th and 9th Edition shells. */
309 if (interactive && cdspelling)
311 temp = cdspell (dirname);
312 if (temp && change_to_directory (temp, no_symlinks))
314 printf ("%s\n", temp);
316 return (bindpwd (no_symlinks));
322 builtin_error ("%s: %s", dirname, strerror (errno));
323 return (EXECUTION_FAILURE);
326 return (bindpwd (no_symlinks));
330 $FUNCTION pwd_builtin
332 Print the current working directory. With the -P option, pwd prints
333 the physical directory, without any symbolic links; the -L option
334 makes pwd follow symbolic links.
337 /* Non-zero means that pwd always prints the physical directory, without
339 static int verbatim_pwd;
341 /* Print the name of the current working directory. */
346 char *directory, *buffer;
349 verbatim_pwd = no_symbolic_links;
350 reset_internal_getopt ();
351 while ((opt = internal_getopt (list, "LP")) != -1)
363 return (EXECUTION_FAILURE);
370 buffer = xmalloc (PATH_MAX);
371 directory = getcwd (buffer, PATH_MAX);
375 builtin_error ("%s: %s", bash_getcwd_errstr, strerror (errno));
380 directory = get_working_directory ("pwd");
384 printf ("%s\n", directory);
387 return (EXECUTION_SUCCESS);
390 return (EXECUTION_FAILURE);
393 /* Do the work of changing to the directory NEWDIR. Handle symbolic
394 link following, etc. */
397 change_to_directory (newdir, nolinks)
405 int chdir_return = 0;
406 char *tdir = (char *)NULL;
408 if (the_current_working_directory == 0)
410 t = get_working_directory ("cd_links");
414 if (the_current_working_directory)
415 t = make_absolute (newdir, the_current_working_directory);
417 t = savestring (newdir);
419 /* TDIR is the canonicalized absolute pathname of the NEWDIR. */
420 tdir = canonicalize_pathname (t);
422 /* Use the canonicalized version of NEWDIR, or, if canonicalization
423 failed, use the non-canonical form. */
432 if (chdir (tdir) < 0)
441 /* We failed changing to the canonicalized directory name. Try
442 what the user passed verbatim. If we succeed, reinitialize
443 the_current_working_directory. */
444 if (chdir (newdir) == 0)
447 if (the_current_working_directory)
449 free (the_current_working_directory);
450 the_current_working_directory = (char *)NULL;
453 tdir = get_working_directory ("cd");
463 FREE (the_current_working_directory);
464 the_current_working_directory = tdir;
467 return (chdir_return);
470 return (chdir (newdir) == 0);
473 /* Code for cd spelling correction. Original patch submitted by
474 Neil Russel (caret@c-side.com). */
483 n = (strlen (dirname) * 3 + 1) / 2 + 1;
486 switch (spname (dirname, guess))
499 * `spname' and its helpers are inspired by the code in "The UNIX
500 * Programming Environment, Kernighan & Pike, Prentice-Hall 1984",
505 * `spname' -- return a correctly spelled filename
507 * int spname(char * oldname, char * newname)
508 * Returns: -1 if no reasonable match found
509 * 0 if exact match found
511 * Stores corrected name in `newname'.
514 spname(oldname, newname)
519 char guess[PATH_MAX + 1], best[PATH_MAX + 1];
525 while (*op == '/') /* Skip slashes */
529 if (*op == '\0') /* Exact or corrected */
531 /* `.' is rarely the right thing. */
532 if (oldname[1] == '\0' && newname[1] == '\0' &&
533 oldname[0] != '.' && newname[0] == '.')
535 return strcmp(oldname, newname) != 0;
538 /* Copy next component into guess */
539 for (p = guess; *op != '/' && *op != '\0'; op++)
540 if (p < guess + PATH_MAX)
544 if (mindist(newname, guess, best) >= 3)
545 return -1; /* Hopeless */
548 * Add to end of newname
550 for (p = best; *np = *p++; np++)
556 * Search directory for a guess
559 mindist(dir, guess, best)
568 dist = 3; /* Worst distance */
572 if ((fd = opendir(dir)) == NULL)
575 while ((dp = readdir(fd)) != NULL)
578 * Look for a better guess. If the new guess is as
579 * good as the current one, we take it. This way,
580 * any single character match will be a better match
583 x = spdist(dp->d_name, guess);
584 if (x <= dist && x != 3)
586 strcpy(best, dp->d_name);
588 if (dist == 0) /* Exact match */
594 /* Don't return `.' */
595 if (best[0] == '.' && best[1] == '\0')
601 * `spdist' -- return the "distance" between two names.
603 * int spname(char * oldname, char * newname)
604 * Returns: 0 if strings are identical
605 * 1 if two characters are transposed
606 * 2 if one character is wrong, added or deleted
616 return 0; /* Exact match */
625 if (cur[1] && new[1] && cur[0] == new[1] && cur[1] == new[0] && strcmp (cur + 2, new + 2) == 0)
626 return 1; /* Transposition */
628 if (strcmp (cur + 1, new + 1) == 0)
629 return 2; /* One character mismatch */
632 if (strcmp(&cur[1], &new[0]) == 0)
633 return 2; /* Extra character */
636 if (*new && strcmp(cur, new + 1) == 0)
637 return 2; /* Missing character */