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 old_anm = array_needs_making;
144 pwdvar = get_string_value ("PWD");
146 tvar = bind_variable ("OLDPWD", pwdvar);
147 if (old_anm == 0 && array_needs_making && exported_p (tvar))
149 update_export_env_inplace ("OLDPWD=", 7, pwdvar);
150 array_needs_making = 0;
153 tvar = bind_variable ("PWD", dirname);
154 if (old_anm == 0 && array_needs_making && exported_p (tvar))
156 update_export_env_inplace ("PWD=", 4, dirname);
157 array_needs_making = 0;
161 return (EXECUTION_SUCCESS);
164 /* This builtin is ultimately the way that all user-visible commands should
165 change the current working directory. It is called by cd_to_string (),
166 so the programming interface is simple, and it handles errors and
167 restrictions properly. */
172 char *dirname, *cdpath, *path, *temp;
173 int path_index, no_symlinks, opt;
176 #if defined (RESTRICTED_SHELL)
179 builtin_error ("restricted");
180 return (EXECUTION_FAILURE);
182 #endif /* RESTRICTED_SHELL */
184 no_symlinks = no_symbolic_links;
185 reset_internal_getopt ();
186 while ((opt = internal_getopt (list, "LP")) != -1)
198 return (EXECUTION_FAILURE);
205 /* `cd' without arguments is equivalent to `cd $HOME' */
206 dirname = get_string_value ("HOME");
210 builtin_error ("HOME not set");
211 return (EXECUTION_FAILURE);
214 if (change_to_directory (dirname, no_symlinks) == 0)
216 builtin_error ("%s: %s", dirname, strerror (errno));
217 return (EXECUTION_FAILURE);
220 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
222 /* This is `cd -', equivalent to `cd $OLDPWD' */
223 dirname = get_string_value ("OLDPWD");
225 if (dirname == 0 || change_to_directory (dirname, no_symlinks) == 0)
228 builtin_error ("OLDPWD not set");
230 builtin_error ("%s: %s", dirname, strerror (errno));
231 return (EXECUTION_FAILURE);
234 printf ("%s\n", dirname);
238 dirname = list->word->word;
240 if (absolute_pathname (dirname) == 0 && (cdpath = get_string_value ("CDPATH")))
242 /* Find directory in $CDPATH. */
244 while (path = extract_colon_unit (cdpath, &path_index))
246 /* OPT is 1 if the path element is non-empty */
247 opt = path[0] != '\0';
248 temp = mkpath (path, dirname, 1);
251 if (stat (temp, &sb) < 0 || S_ISDIR (sb.st_mode) == 0)
257 if (change_to_directory (temp, no_symlinks))
259 /* POSIX.2 says that if a nonempty directory from CDPATH
260 is used to find the directory to change to, the new
261 directory name is echoed to stdout, whether or not
262 the shell is interactive. */
264 printf ("%s\n", no_symlinks ? temp : the_current_working_directory);
267 /* Posix.2 says that after using CDPATH, the resultant
268 value of $PWD will not contain symlinks. */
269 return (bindpwd (posixly_correct || no_symlinks));
275 /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't
276 try the current directory, so we just punt now with an error
277 message if POSIXLY_CORRECT is non-zero. The check for cdpath[0]
278 is so we don't mistakenly treat a CDPATH value of "" as not
279 specifying the current directory. */
280 if (posixly_correct && cdpath[0])
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 */