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 char *bash_getcwd_errstr;
54 static int change_to_directory ();
56 static char *cdspell ();
57 static int spname (), mindist (), spdist ();
64 $SHORT_DOC cd [-PL] [dir]
65 Change the current directory to DIR. The variable $HOME is the
66 default DIR. The variable $CDPATH defines the search path for
67 the directory containing DIR. Alternative directory names in CDPATH
68 are separated by a colon (:). A null directory name is the same as
69 the current directory, i.e. `.'. If DIR begins with a slash (/),
70 then $CDPATH is not used. If the directory is not found, and the
71 shell option `cdable_vars' is set, then try the word as a variable
72 name. If that variable has a value, then cd to the value of that
73 variable. The -P option says to use the physical directory structure
74 instead of following symbolic links; the -L option forces symbolic links
78 /* Take PATH, an element from $CDPATH, and DIR, a directory name, and paste
79 them together into PATH/DIR. Tilde expansion is performed on PATH if
80 DOTILDE is non-zero. If PATH is the empty string, it is converted to
81 `./', since a null element in $CDPATH means the current directory. */
83 mkpath (path, dir, dotilde)
99 xpath = (dotilde && *path == '~') ? bash_tilde_expand (path) : path;
100 pathlen = strlen (xpath);
103 dirlen = strlen (dir);
104 ret = xmalloc (2 + dirlen + pathlen);
106 if (xpath[pathlen - 1] != '/')
108 ret[pathlen++] = '/';
111 strcpy (ret + pathlen, dir);
118 bindpwd (no_symlinks)
126 old_symlinks = no_symbolic_links;
127 no_symbolic_links = 1;
128 dirname = get_working_directory ("cd");
129 no_symbolic_links = old_symlinks;
132 dirname = get_working_directory ("cd");
134 bind_variable ("OLDPWD", get_string_value ("PWD"));
135 bind_variable ("PWD", dirname);
138 return (EXECUTION_SUCCESS);
141 /* This builtin is ultimately the way that all user-visible commands should
142 change the current working directory. It is called by cd_to_string (),
143 so the programming interface is simple, and it handles errors and
144 restrictions properly. */
149 char *dirname, *cdpath, *path, *temp;
150 int path_index, no_symlinks, opt;
153 #if defined (RESTRICTED_SHELL)
156 builtin_error ("restricted");
157 return (EXECUTION_FAILURE);
159 #endif /* RESTRICTED_SHELL */
161 no_symlinks = no_symbolic_links;
162 reset_internal_getopt ();
163 while ((opt = internal_getopt (list, "LP")) != -1)
175 return (EXECUTION_FAILURE);
182 /* `cd' without arguments is equivalent to `cd $HOME' */
183 dirname = get_string_value ("HOME");
187 builtin_error ("HOME not set");
188 return (EXECUTION_FAILURE);
191 if (change_to_directory (dirname, no_symlinks) == 0)
193 builtin_error ("%s: %s", dirname, strerror (errno));
194 return (EXECUTION_FAILURE);
197 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
199 /* This is `cd -', equivalent to `cd $OLDPWD' */
200 dirname = get_string_value ("OLDPWD");
202 if (dirname == 0 || change_to_directory (dirname, no_symlinks) == 0)
205 builtin_error ("OLDPWD not set");
207 builtin_error ("%s: %s", dirname, strerror (errno));
208 return (EXECUTION_FAILURE);
213 dirname = list->word->word;
215 if (absolute_pathname (dirname) == 0 && (cdpath = get_string_value ("CDPATH")))
217 /* Find directory in $CDPATH. */
219 while ((path = extract_colon_unit (cdpath, &path_index)))
221 temp = mkpath (path, dirname, 1);
224 if (stat (temp, &sb) < 0 || S_ISDIR (sb.st_mode) == 0)
230 if (change_to_directory (temp, no_symlinks))
232 if (temp[0] != '.' || temp[1] != '/')
233 printf ("%s\n", temp);
236 /* Posix.2 says that after using CDPATH, the resultant
237 value of $PWD will not contain symlinks. */
238 return (bindpwd (posixly_correct));
245 if (change_to_directory (dirname, no_symlinks))
246 return (bindpwd (no_symlinks));
248 /* If the user requests it, then perhaps this is the name of
249 a shell variable, whose value contains the directory to
250 change to. If that is the case, then change to that
254 temp = get_string_value (dirname);
255 if (temp && change_to_directory (temp, no_symlinks))
257 printf ("%s\n", temp);
258 return (bindpwd (no_symlinks));
262 /* If the user requests it, try to find a directory name similar in
263 spelling to the one requested, in case the user made a simple
264 typo. This is similar to the UNIX 8th and 9th Edition shells. */
265 if (interactive && cdspelling)
267 temp = cdspell (dirname);
268 if (temp && change_to_directory (temp, no_symlinks))
270 printf ("%s\n", temp);
272 return (bindpwd (no_symlinks));
278 builtin_error ("%s: %s", dirname, strerror (errno));
279 return (EXECUTION_FAILURE);
282 return (bindpwd (no_symlinks));
286 $FUNCTION pwd_builtin
288 Print the current working directory. With the -P option, pwd prints
289 the physical directory, without any symbolic links; the -L option
290 makes pwd follow symbolic links.
293 /* Non-zero means that pwd always prints the physical directory, without
295 static int verbatim_pwd;
297 /* Print the name of the current working directory. */
302 char *directory, *buffer;
305 verbatim_pwd = no_symbolic_links;
306 reset_internal_getopt ();
307 while ((opt = internal_getopt (list, "LP")) != -1)
319 return (EXECUTION_FAILURE);
326 buffer = xmalloc (PATH_MAX);
327 directory = getcwd (buffer, PATH_MAX);
331 builtin_error ("%s: %s", bash_getcwd_errstr, strerror (errno));
336 directory = get_working_directory ("pwd");
340 printf ("%s\n", directory);
343 return (EXECUTION_SUCCESS);
346 return (EXECUTION_FAILURE);
349 /* Do the work of changing to the directory NEWDIR. Handle symbolic
350 link following, etc. */
353 change_to_directory (newdir, nolinks)
361 int chdir_return = 0;
362 char *tdir = (char *)NULL;
364 if (the_current_working_directory == 0)
366 t = get_working_directory ("cd_links");
370 if (the_current_working_directory)
371 t = make_absolute (newdir, the_current_working_directory);
373 t = savestring (newdir);
375 /* TDIR is the canonicalized absolute pathname of the NEWDIR. */
376 tdir = canonicalize_pathname (t);
378 /* Use the canonicalized version of NEWDIR, or, if canonicalization
379 failed, use the non-canonical form. */
388 if (chdir (tdir) < 0)
397 /* We failed changing to the canonicalized directory name. Try
398 what the user passed verbatim. If we succeed, reinitialize
399 the_current_working_directory. */
400 if (chdir (newdir) == 0)
403 if (the_current_working_directory)
405 free (the_current_working_directory);
406 the_current_working_directory = (char *)NULL;
409 tdir = get_working_directory ("cd");
419 FREE (the_current_working_directory);
420 the_current_working_directory = tdir;
423 return (chdir_return);
426 return (chdir (newdir) == 0);
429 /* Code for cd spelling correction. Original patch submitted by
430 Neil Russel (caret@c-side.com). */
439 n = (strlen (dirname) * 3 + 1) / 2 + 1;
442 switch (spname (dirname, guess))
455 * `spname' and its helpers are inspired by the code in "The UNIX
456 * Programming Environment, Kernighan & Pike, Prentice-Hall 1984",
461 * `spname' -- return a correctly spelled filename
463 * int spname(char * oldname, char * newname)
464 * Returns: -1 if no reasonable match found
465 * 0 if exact match found
467 * Stores corrected name in `newname'.
470 spname(oldname, newname)
475 char guess[PATH_MAX + 1], best[PATH_MAX + 1];
481 while (*op == '/') /* Skip slashes */
485 if (*op == '\0') /* Exact or corrected */
487 /* `.' is rarely the right thing. */
488 if (oldname[1] == '\0' && newname[1] == '\0' &&
489 oldname[0] != '.' && newname[0] == '.')
491 return strcmp(oldname, newname) != 0;
494 /* Copy next component into guess */
495 for (p = guess; *op != '/' && *op != '\0'; op++)
496 if (p < guess + PATH_MAX)
500 if (mindist(newname, guess, best) >= 3)
501 return -1; /* Hopeless */
504 * Add to end of newname
506 for (p = best; *np = *p++; np++)
512 * Search directory for a guess
515 mindist(dir, guess, best)
524 dist = 3; /* Worst distance */
528 if ((fd = opendir(dir)) == NULL)
531 while ((dp = readdir(fd)) != NULL)
534 * Look for a better guess. If the new guess is as
535 * good as the current one, we take it. This way,
536 * any single character match will be a better match
539 x = spdist(dp->d_name, guess);
540 if (x <= dist && x != 3)
542 strcpy(best, dp->d_name);
544 if (dist == 0) /* Exact match */
554 * `spdist' -- return the "distance" between two names.
556 * int spname(char * oldname, char * newname)
557 * Returns: 0 if strings are identical
558 * 1 if two characters are transposed
559 * 2 if one character is wrong, added or deleted
569 return 0; /* Exact match */
578 if (cur[1] && new[1] && cur[0] == new[1] && cur[1] == new[0] && strcmp (cur + 2, new + 2) == 0)
579 return 1; /* Transposition */
581 if (strcmp (cur + 1, new + 1) == 0)
582 return 2; /* One character mismatch */
585 if (strcmp(&cur[1], &new[0]) == 0)
586 return 2; /* Extra character */
589 if (*new && strcmp(cur, new + 1) == 0)
590 return 2; /* Missing character */