]> git.ipfire.org Git - thirdparty/bash.git/blob - builtins/cd.def
76ef14ba3ec77f72a082d7077e6193f80b8a427b
[thirdparty/bash.git] / builtins / cd.def
1 This file is cd.def, from which is created cd.c. It implements the
2 builtins "cd" and "pwd" in Bash.
3
4 Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
5
6 This file is part of GNU Bash, the Bourne Again SHell.
7
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
11 version.
12
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
16 for more details.
17
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.
21
22 $PRODUCES cd.c
23 #include <config.h>
24
25 #if defined (HAVE_UNISTD_H)
26 # ifdef _MINIX
27 # include <sys/types.h>
28 # endif
29 # include <unistd.h>
30 #endif
31
32 #include "../bashtypes.h"
33 #include "../posixdir.h"
34 #include "../posixstat.h"
35 #ifndef _MINIX
36 #include <sys/param.h>
37 #endif
38
39 #include <stdio.h>
40
41 #include "../bashansi.h"
42
43 #include <errno.h>
44 #include <tilde/tilde.h>
45
46 #include "../shell.h"
47 #include "../flags.h"
48 #include "../maxpath.h"
49 #include "common.h"
50 #include "bashgetopt.h"
51
52 #if !defined (errno)
53 extern int errno;
54 #endif /* !errno */
55
56 extern int posixly_correct, interactive;
57 extern int array_needs_making;
58 extern char *bash_getcwd_errstr;
59
60 static int change_to_directory ();
61
62 static char *cdspell ();
63 static int spname (), mindist (), spdist ();
64
65 /* Change this to 1 to get cd spelling correction by default. */
66 int cdspelling = 0;
67
68 int cdable_vars;
69
70 $BUILTIN cd
71 $FUNCTION cd_builtin
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
83 to be followed.
84 $END
85
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. */
90 static char *
91 mkpath (path, dir, dotilde)
92 char *path, *dir;
93 int dotilde;
94 {
95 int dirlen, pathlen;
96 char *ret, *xpath;
97
98 if (*path == '\0')
99 {
100 xpath = xmalloc (2);
101 xpath[0] = '.';
102 xpath[1] = '\0';
103 pathlen = 1;
104 }
105 else
106 {
107 xpath = (dotilde && *path == '~') ? bash_tilde_expand (path) : path;
108 pathlen = strlen (xpath);
109 }
110
111 dirlen = strlen (dir);
112 ret = xmalloc (2 + dirlen + pathlen);
113 strcpy (ret, xpath);
114 if (xpath[pathlen - 1] != '/')
115 {
116 ret[pathlen++] = '/';
117 ret[pathlen] = '\0';
118 }
119 strcpy (ret + pathlen, dir);
120 if (xpath != path)
121 free (xpath);
122 return (ret);
123 }
124
125 static int
126 bindpwd (no_symlinks)
127 int no_symlinks;
128 {
129 char *dirname, *pwdvar;
130 int old_symlinks, old_anm;
131 SHELL_VAR *tvar;
132
133 if (no_symlinks)
134 {
135 old_symlinks = no_symbolic_links;
136 no_symbolic_links = 1;
137 dirname = get_working_directory ("cd");
138 no_symbolic_links = old_symlinks;
139 }
140 else
141 dirname = get_working_directory ("cd");
142
143 bind_variable ("OLDPWD", get_string_value ("PWD"));
144
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
151 needing a remake. */
152 if (old_anm == 0 && array_needs_making && exported_p (tvar))
153 {
154 pwdvar = xmalloc (STRLEN (dirname) + 5); /* 5 = "PWD" + '=' + '\0' */
155 strcpy (pwdvar, "PWD=");
156 if (dirname)
157 strcpy (pwdvar + 4, dirname);
158 add_or_supercede_exported_var (pwdvar, 0);
159 array_needs_making = 0;
160 }
161
162 FREE (dirname);
163 return (EXECUTION_SUCCESS);
164 }
165
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. */
170 int
171 cd_builtin (list)
172 WORD_LIST *list;
173 {
174 char *dirname, *cdpath, *path, *temp;
175 int path_index, no_symlinks, opt;
176 struct stat sb;
177
178 #if defined (RESTRICTED_SHELL)
179 if (restricted)
180 {
181 builtin_error ("restricted");
182 return (EXECUTION_FAILURE);
183 }
184 #endif /* RESTRICTED_SHELL */
185
186 no_symlinks = no_symbolic_links;
187 reset_internal_getopt ();
188 while ((opt = internal_getopt (list, "LP")) != -1)
189 {
190 switch (opt)
191 {
192 case 'P':
193 no_symlinks = 1;
194 break;
195 case 'L':
196 no_symlinks = 0;
197 break;
198 default:
199 builtin_usage ();
200 return (EXECUTION_FAILURE);
201 }
202 }
203 list = loptend;
204
205 if (list == 0)
206 {
207 /* `cd' without arguments is equivalent to `cd $HOME' */
208 dirname = get_string_value ("HOME");
209
210 if (dirname == 0)
211 {
212 builtin_error ("HOME not set");
213 return (EXECUTION_FAILURE);
214 }
215
216 if (change_to_directory (dirname, no_symlinks) == 0)
217 {
218 builtin_error ("%s: %s", dirname, strerror (errno));
219 return (EXECUTION_FAILURE);
220 }
221 }
222 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
223 {
224 /* This is `cd -', equivalent to `cd $OLDPWD' */
225 dirname = get_string_value ("OLDPWD");
226
227 if (dirname == 0 || change_to_directory (dirname, no_symlinks) == 0)
228 {
229 if (dirname == 0)
230 builtin_error ("OLDPWD not set");
231 else
232 builtin_error ("%s: %s", dirname, strerror (errno));
233 return (EXECUTION_FAILURE);
234 }
235 if (interactive)
236 printf ("%s\n", dirname);
237 }
238 else
239 {
240 dirname = list->word->word;
241
242 if (absolute_pathname (dirname) == 0 && (cdpath = get_string_value ("CDPATH")))
243 {
244 /* Find directory in $CDPATH. */
245 path_index = 0;
246 while ((path = extract_colon_unit (cdpath, &path_index)))
247 {
248 /* OPT is 1 if the path element is non-empty */
249 opt = path[0] != '\0';
250 temp = mkpath (path, dirname, 1);
251 free (path);
252
253 if (stat (temp, &sb) < 0 || S_ISDIR (sb.st_mode) == 0)
254 {
255 free (temp);
256 continue;
257 }
258
259 if (change_to_directory (temp, no_symlinks))
260 {
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. */
265 if (opt)
266 printf ("%s\n", no_symlinks ? temp : the_current_working_directory);
267
268 free (temp);
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));
272 }
273 else
274 free (temp);
275 }
276
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])
283 {
284 builtin_error ("%s: %s", dirname, strerror (ENOENT));
285 return (EXECUTION_FAILURE);
286 }
287 }
288
289 if (change_to_directory (dirname, no_symlinks))
290 return (bindpwd (no_symlinks));
291
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
295 directory. */
296 if (cdable_vars)
297 {
298 temp = get_string_value (dirname);
299 if (temp && change_to_directory (temp, no_symlinks))
300 {
301 printf ("%s\n", temp);
302 return (bindpwd (no_symlinks));
303 }
304 }
305
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)
310 {
311 temp = cdspell (dirname);
312 if (temp && change_to_directory (temp, no_symlinks))
313 {
314 printf ("%s\n", temp);
315 free (temp);
316 return (bindpwd (no_symlinks));
317 }
318 else
319 FREE (temp);
320 }
321
322 builtin_error ("%s: %s", dirname, strerror (errno));
323 return (EXECUTION_FAILURE);
324 }
325
326 return (bindpwd (no_symlinks));
327 }
328
329 $BUILTIN pwd
330 $FUNCTION pwd_builtin
331 $SHORT_DOC pwd [-PL]
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.
335 $END
336
337 /* Non-zero means that pwd always prints the physical directory, without
338 symbolic links. */
339 static int verbatim_pwd;
340
341 /* Print the name of the current working directory. */
342 int
343 pwd_builtin (list)
344 WORD_LIST *list;
345 {
346 char *directory, *buffer;
347 int opt;
348
349 verbatim_pwd = no_symbolic_links;
350 reset_internal_getopt ();
351 while ((opt = internal_getopt (list, "LP")) != -1)
352 {
353 switch (opt)
354 {
355 case 'P':
356 verbatim_pwd = 1;
357 break;
358 case 'L':
359 verbatim_pwd = 0;
360 break;
361 default:
362 builtin_usage ();
363 return (EXECUTION_FAILURE);
364 }
365 }
366 list = loptend;
367
368 if (verbatim_pwd)
369 {
370 buffer = xmalloc (PATH_MAX);
371 directory = getcwd (buffer, PATH_MAX);
372
373 if (directory == 0)
374 {
375 builtin_error ("%s: %s", bash_getcwd_errstr, strerror (errno));
376 free (buffer);
377 }
378 }
379 else
380 directory = get_working_directory ("pwd");
381
382 if (directory)
383 {
384 printf ("%s\n", directory);
385 fflush (stdout);
386 free (directory);
387 return (EXECUTION_SUCCESS);
388 }
389 else
390 return (EXECUTION_FAILURE);
391 }
392
393 /* Do the work of changing to the directory NEWDIR. Handle symbolic
394 link following, etc. */
395
396 static int
397 change_to_directory (newdir, nolinks)
398 char *newdir;
399 int nolinks;
400 {
401 char *t;
402
403 if (nolinks == 0)
404 {
405 int chdir_return = 0;
406 char *tdir = (char *)NULL;
407
408 if (the_current_working_directory == 0)
409 {
410 t = get_working_directory ("cd_links");
411 FREE (t);
412 }
413
414 if (the_current_working_directory)
415 t = make_absolute (newdir, the_current_working_directory);
416 else
417 t = savestring (newdir);
418
419 /* TDIR is the canonicalized absolute pathname of the NEWDIR. */
420 tdir = canonicalize_pathname (t);
421
422 /* Use the canonicalized version of NEWDIR, or, if canonicalization
423 failed, use the non-canonical form. */
424 if (tdir && *tdir)
425 free (t);
426 else
427 {
428 FREE (tdir);
429 tdir = t;
430 }
431
432 if (chdir (tdir) < 0)
433 {
434 int err;
435
436 chdir_return = 0;
437 free (tdir);
438
439 err = errno;
440
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)
445 {
446 chdir_return = 1;
447 if (the_current_working_directory)
448 {
449 free (the_current_working_directory);
450 the_current_working_directory = (char *)NULL;
451 }
452
453 tdir = get_working_directory ("cd");
454 FREE (tdir);
455 }
456 else
457 errno = err;
458 }
459 else
460 {
461 chdir_return = 1;
462
463 FREE (the_current_working_directory);
464 the_current_working_directory = tdir;
465 }
466
467 return (chdir_return);
468 }
469 else
470 return (chdir (newdir) == 0);
471 }
472
473 /* Code for cd spelling correction. Original patch submitted by
474 Neil Russel (caret@c-side.com). */
475
476 static char *
477 cdspell (dirname)
478 char *dirname;
479 {
480 int n;
481 char *guess;
482
483 n = (strlen (dirname) * 3 + 1) / 2 + 1;
484 guess = xmalloc (n);
485
486 switch (spname (dirname, guess))
487 {
488 case -1:
489 default:
490 free (guess);
491 return (char *)NULL;
492 case 0:
493 case 1:
494 return guess;
495 }
496 }
497
498 /*
499 * `spname' and its helpers are inspired by the code in "The UNIX
500 * Programming Environment, Kernighan & Pike, Prentice-Hall 1984",
501 * pages 209 - 213.
502 */
503
504 /*
505 * `spname' -- return a correctly spelled filename
506 *
507 * int spname(char * oldname, char * newname)
508 * Returns: -1 if no reasonable match found
509 * 0 if exact match found
510 * 1 if corrected
511 * Stores corrected name in `newname'.
512 */
513 static int
514 spname(oldname, newname)
515 char *oldname;
516 char *newname;
517 {
518 char *op, *np, *p;
519 char guess[PATH_MAX + 1], best[PATH_MAX + 1];
520
521 op = oldname;
522 np = newname;
523 for (;;)
524 {
525 while (*op == '/') /* Skip slashes */
526 *np++ = *op++;
527 *np = '\0';
528
529 if (*op == '\0') /* Exact or corrected */
530 {
531 /* `.' is rarely the right thing. */
532 if (oldname[1] == '\0' && newname[1] == '\0' &&
533 oldname[0] != '.' && newname[0] == '.')
534 return -1;
535 return strcmp(oldname, newname) != 0;
536 }
537
538 /* Copy next component into guess */
539 for (p = guess; *op != '/' && *op != '\0'; op++)
540 if (p < guess + PATH_MAX)
541 *p++ = *op;
542 *p = '\0';
543
544 if (mindist(newname, guess, best) >= 3)
545 return -1; /* Hopeless */
546
547 /*
548 * Add to end of newname
549 */
550 for (p = best; *np = *p++; np++)
551 ;
552 }
553 }
554
555 /*
556 * Search directory for a guess
557 */
558 static int
559 mindist(dir, guess, best)
560 char *dir;
561 char *guess;
562 char *best;
563 {
564 DIR *fd;
565 struct dirent *dp;
566 int dist, x;
567
568 dist = 3; /* Worst distance */
569 if (*dir == '\0')
570 dir = ".";
571
572 if ((fd = opendir(dir)) == NULL)
573 return dist;
574
575 while ((dp = readdir(fd)) != NULL)
576 {
577 /*
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
581 * than ".".
582 */
583 x = spdist(dp->d_name, guess);
584 if (x <= dist && x != 3)
585 {
586 strcpy(best, dp->d_name);
587 dist = x;
588 if (dist == 0) /* Exact match */
589 break;
590 }
591 }
592 (void)closedir(fd);
593
594 /* Don't return `.' */
595 if (best[0] == '.' && best[1] == '\0')
596 dist = 3;
597 return dist;
598 }
599
600 /*
601 * `spdist' -- return the "distance" between two names.
602 *
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
607 * 3 otherwise
608 */
609 static int
610 spdist(cur, new)
611 char *cur, *new;
612 {
613 while (*cur == *new)
614 {
615 if (*cur == '\0')
616 return 0; /* Exact match */
617 cur++;
618 new++;
619 }
620
621 if (*cur)
622 {
623 if (*new)
624 {
625 if (cur[1] && new[1] && cur[0] == new[1] && cur[1] == new[0] && strcmp (cur + 2, new + 2) == 0)
626 return 1; /* Transposition */
627
628 if (strcmp (cur + 1, new + 1) == 0)
629 return 2; /* One character mismatch */
630 }
631
632 if (strcmp(&cur[1], &new[0]) == 0)
633 return 2; /* Extra character */
634 }
635
636 if (*new && strcmp(cur, new + 1) == 0)
637 return 2; /* Missing character */
638
639 return 3;
640 }