]>
Commit | Line | Data |
---|---|---|
726f6388 | 1 | This file is cd.def, from which is created cd.c. It implements the |
ccc6cda3 | 2 | builtins "cd" and "pwd" in Bash. |
726f6388 | 3 | |
b80f6443 | 4 | Copyright (C) 1987-2003 Free Software Foundation, Inc. |
726f6388 JA |
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 | |
bb70624e | 10 | Software Foundation; either version 2, or (at your option) any later |
726f6388 JA |
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 | |
bb70624e | 20 | Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. |
726f6388 JA |
21 | |
22 | $PRODUCES cd.c | |
ccc6cda3 | 23 | #include <config.h> |
726f6388 | 24 | |
ccc6cda3 | 25 | #if defined (HAVE_UNISTD_H) |
cce855bc JA |
26 | # ifdef _MINIX |
27 | # include <sys/types.h> | |
28 | # endif | |
ccc6cda3 JA |
29 | # include <unistd.h> |
30 | #endif | |
31 | ||
32 | #include "../bashtypes.h" | |
bb70624e JA |
33 | #include "posixdir.h" |
34 | #include "posixstat.h" | |
cce855bc | 35 | #ifndef _MINIX |
726f6388 | 36 | #include <sys/param.h> |
cce855bc | 37 | #endif |
726f6388 | 38 | |
ccc6cda3 JA |
39 | #include <stdio.h> |
40 | ||
41 | #include "../bashansi.h" | |
b80f6443 | 42 | #include "../bashintl.h" |
726f6388 JA |
43 | |
44 | #include <errno.h> | |
45 | #include <tilde/tilde.h> | |
46 | ||
47 | #include "../shell.h" | |
48 | #include "../flags.h" | |
bb70624e | 49 | #include "maxpath.h" |
726f6388 | 50 | #include "common.h" |
ccc6cda3 | 51 | #include "bashgetopt.h" |
726f6388 JA |
52 | |
53 | #if !defined (errno) | |
54 | extern int errno; | |
55 | #endif /* !errno */ | |
56 | ||
f73dda09 | 57 | extern int posixly_correct; |
d166f048 | 58 | extern int array_needs_making; |
ccc6cda3 JA |
59 | extern char *bash_getcwd_errstr; |
60 | ||
f73dda09 JA |
61 | static int bindpwd __P((int)); |
62 | static int change_to_directory __P((char *, int)); | |
ccc6cda3 | 63 | |
f73dda09 | 64 | static char *cdspell __P((char *)); |
d166f048 JA |
65 | |
66 | /* Change this to 1 to get cd spelling correction by default. */ | |
67 | int cdspelling = 0; | |
ccc6cda3 JA |
68 | |
69 | int cdable_vars; | |
726f6388 JA |
70 | |
71 | $BUILTIN cd | |
72 | $FUNCTION cd_builtin | |
7117c2d2 | 73 | $SHORT_DOC cd [-L|-P] [dir] |
726f6388 | 74 | Change the current directory to DIR. The variable $HOME is the |
cce855bc | 75 | default DIR. The variable CDPATH defines the search path for |
ccc6cda3 JA |
76 | the directory containing DIR. Alternative directory names in CDPATH |
77 | are separated by a colon (:). A null directory name is the same as | |
726f6388 | 78 | the current directory, i.e. `.'. If DIR begins with a slash (/), |
cce855bc | 79 | then CDPATH is not used. If the directory is not found, and the |
ccc6cda3 | 80 | shell option `cdable_vars' is set, then try the word as a variable |
726f6388 | 81 | name. If that variable has a value, then cd to the value of that |
ccc6cda3 JA |
82 | variable. The -P option says to use the physical directory structure |
83 | instead of following symbolic links; the -L option forces symbolic links | |
84 | to be followed. | |
726f6388 JA |
85 | $END |
86 | ||
ccc6cda3 JA |
87 | static int |
88 | bindpwd (no_symlinks) | |
89 | int no_symlinks; | |
90 | { | |
d166f048 | 91 | char *dirname, *pwdvar; |
f73dda09 | 92 | int old_anm; |
d166f048 | 93 | SHELL_VAR *tvar; |
ccc6cda3 | 94 | |
28ef6c31 JA |
95 | #define tcwd the_current_working_directory |
96 | dirname = tcwd ? (no_symlinks ? sh_physpath (tcwd, 0) : tcwd) | |
97 | : get_working_directory ("cd"); | |
98 | #undef tcwd | |
ccc6cda3 | 99 | |
d166f048 | 100 | old_anm = array_needs_making; |
b72432fd JA |
101 | pwdvar = get_string_value ("PWD"); |
102 | ||
103 | tvar = bind_variable ("OLDPWD", pwdvar); | |
104 | if (old_anm == 0 && array_needs_making && exported_p (tvar)) | |
105 | { | |
106 | update_export_env_inplace ("OLDPWD=", 7, pwdvar); | |
107 | array_needs_making = 0; | |
108 | } | |
109 | ||
b80f6443 | 110 | tvar = bind_variable ("PWD", dirname ? dirname : ""); |
d166f048 JA |
111 | if (old_anm == 0 && array_needs_making && exported_p (tvar)) |
112 | { | |
b80f6443 | 113 | update_export_env_inplace ("PWD=", 4, dirname ? dirname : ""); |
d166f048 JA |
114 | array_needs_making = 0; |
115 | } | |
ccc6cda3 | 116 | |
28ef6c31 JA |
117 | if (dirname && dirname != the_current_working_directory) |
118 | free (dirname); | |
b80f6443 | 119 | |
ccc6cda3 JA |
120 | return (EXECUTION_SUCCESS); |
121 | } | |
122 | ||
7117c2d2 JA |
123 | /* Call get_working_directory to reset the value of |
124 | the_current_working_directory () */ | |
125 | static char * | |
b80f6443 JA |
126 | resetpwd (caller) |
127 | char *caller; | |
7117c2d2 JA |
128 | { |
129 | char *tdir; | |
130 | ||
131 | FREE (the_current_working_directory); | |
132 | the_current_working_directory = (char *)NULL; | |
b80f6443 | 133 | tdir = get_working_directory (caller); |
7117c2d2 JA |
134 | return (tdir); |
135 | } | |
136 | ||
28ef6c31 JA |
137 | #define LCD_DOVARS 0x001 |
138 | #define LCD_DOSPELL 0x002 | |
139 | #define LCD_PRINTPATH 0x004 | |
140 | #define LCD_FREEDIRNAME 0x010 | |
141 | ||
726f6388 JA |
142 | /* This builtin is ultimately the way that all user-visible commands should |
143 | change the current working directory. It is called by cd_to_string (), | |
144 | so the programming interface is simple, and it handles errors and | |
145 | restrictions properly. */ | |
146 | int | |
147 | cd_builtin (list) | |
148 | WORD_LIST *list; | |
149 | { | |
ccc6cda3 | 150 | char *dirname, *cdpath, *path, *temp; |
28ef6c31 | 151 | int path_index, no_symlinks, opt, lflag; |
726f6388 JA |
152 | |
153 | #if defined (RESTRICTED_SHELL) | |
154 | if (restricted) | |
155 | { | |
7117c2d2 | 156 | sh_restricted ((char *)NULL); |
726f6388 JA |
157 | return (EXECUTION_FAILURE); |
158 | } | |
159 | #endif /* RESTRICTED_SHELL */ | |
160 | ||
ccc6cda3 JA |
161 | no_symlinks = no_symbolic_links; |
162 | reset_internal_getopt (); | |
163 | while ((opt = internal_getopt (list, "LP")) != -1) | |
726f6388 | 164 | { |
ccc6cda3 | 165 | switch (opt) |
726f6388 | 166 | { |
ccc6cda3 JA |
167 | case 'P': |
168 | no_symlinks = 1; | |
169 | break; | |
170 | case 'L': | |
171 | no_symlinks = 0; | |
172 | break; | |
173 | default: | |
174 | builtin_usage (); | |
726f6388 JA |
175 | return (EXECUTION_FAILURE); |
176 | } | |
726f6388 | 177 | } |
ccc6cda3 JA |
178 | list = loptend; |
179 | ||
28ef6c31 JA |
180 | lflag = (cdable_vars ? LCD_DOVARS : 0) | |
181 | ((interactive && cdspelling) ? LCD_DOSPELL : 0); | |
182 | ||
ccc6cda3 | 183 | if (list == 0) |
726f6388 | 184 | { |
ccc6cda3 | 185 | /* `cd' without arguments is equivalent to `cd $HOME' */ |
726f6388 JA |
186 | dirname = get_string_value ("HOME"); |
187 | ||
ccc6cda3 | 188 | if (dirname == 0) |
726f6388 | 189 | { |
b80f6443 | 190 | builtin_error (_("HOME not set")); |
726f6388 JA |
191 | return (EXECUTION_FAILURE); |
192 | } | |
28ef6c31 | 193 | lflag = 0; |
726f6388 | 194 | } |
ccc6cda3 | 195 | else if (list->word->word[0] == '-' && list->word->word[1] == '\0') |
726f6388 | 196 | { |
ccc6cda3 JA |
197 | /* This is `cd -', equivalent to `cd $OLDPWD' */ |
198 | dirname = get_string_value ("OLDPWD"); | |
726f6388 | 199 | |
28ef6c31 | 200 | if (dirname == 0) |
726f6388 | 201 | { |
b80f6443 | 202 | builtin_error (_("OLDPWD not set")); |
726f6388 JA |
203 | return (EXECUTION_FAILURE); |
204 | } | |
b80f6443 | 205 | #if 0 |
28ef6c31 | 206 | lflag = interactive ? LCD_PRINTPATH : 0; |
b80f6443 JA |
207 | #else |
208 | lflag = LCD_PRINTPATH; /* According to SUSv3 */ | |
209 | #endif | |
726f6388 | 210 | } |
28ef6c31 JA |
211 | else if (absolute_pathname (list->word->word)) |
212 | dirname = list->word->word; | |
213 | else if (cdpath = get_string_value ("CDPATH")) | |
726f6388 | 214 | { |
ccc6cda3 JA |
215 | dirname = list->word->word; |
216 | ||
28ef6c31 JA |
217 | /* Find directory in $CDPATH. */ |
218 | path_index = 0; | |
219 | while (path = extract_colon_unit (cdpath, &path_index)) | |
726f6388 | 220 | { |
28ef6c31 JA |
221 | /* OPT is 1 if the path element is non-empty */ |
222 | opt = path[0] != '\0'; | |
223 | temp = sh_makepath (path, dirname, MP_DOTILDE); | |
224 | free (path); | |
cce855bc | 225 | |
28ef6c31 | 226 | if (change_to_directory (temp, no_symlinks)) |
cce855bc | 227 | { |
28ef6c31 JA |
228 | /* POSIX.2 says that if a nonempty directory from CDPATH |
229 | is used to find the directory to change to, the new | |
230 | directory name is echoed to stdout, whether or not | |
231 | the shell is interactive. */ | |
b80f6443 JA |
232 | if (opt && (path = no_symlinks ? temp : the_current_working_directory)) |
233 | printf ("%s\n", path); | |
28ef6c31 JA |
234 | |
235 | free (temp); | |
236 | /* Posix.2 says that after using CDPATH, the resultant | |
237 | value of $PWD will not contain `.' or `..'. */ | |
238 | return (bindpwd (posixly_correct || no_symlinks)); | |
cce855bc | 239 | } |
28ef6c31 JA |
240 | else |
241 | free (temp); | |
726f6388 JA |
242 | } |
243 | ||
28ef6c31 JA |
244 | /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't |
245 | try the current directory, so we just punt now with an error | |
246 | message if POSIXLY_CORRECT is non-zero. The check for cdpath[0] | |
247 | is so we don't mistakenly treat a CDPATH value of "" as not | |
248 | specifying the current directory. */ | |
249 | if (posixly_correct && cdpath[0]) | |
726f6388 | 250 | { |
28ef6c31 JA |
251 | builtin_error ("%s: %s", dirname, strerror (ENOENT)); |
252 | return (EXECUTION_FAILURE); | |
726f6388 | 253 | } |
28ef6c31 JA |
254 | } |
255 | else | |
256 | dirname = list->word->word; | |
257 | ||
258 | /* When we get here, DIRNAME is the directory to change to. If we | |
259 | chdir successfully, just return. */ | |
260 | if (change_to_directory (dirname, no_symlinks)) | |
261 | { | |
262 | if (lflag & LCD_PRINTPATH) | |
263 | printf ("%s\n", dirname); | |
264 | return (bindpwd (no_symlinks)); | |
265 | } | |
726f6388 | 266 | |
28ef6c31 JA |
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. */ | |
270 | if (lflag & LCD_DOVARS) | |
271 | { | |
272 | temp = get_string_value (dirname); | |
273 | if (temp && change_to_directory (temp, no_symlinks)) | |
726f6388 | 274 | { |
28ef6c31 JA |
275 | printf ("%s\n", temp); |
276 | return (bindpwd (no_symlinks)); | |
726f6388 | 277 | } |
28ef6c31 | 278 | } |
726f6388 | 279 | |
28ef6c31 JA |
280 | /* If the user requests it, try to find a directory name similar in |
281 | spelling to the one requested, in case the user made a simple | |
282 | typo. This is similar to the UNIX 8th and 9th Edition shells. */ | |
283 | if (lflag & LCD_DOSPELL) | |
284 | { | |
285 | temp = cdspell (dirname); | |
286 | if (temp && change_to_directory (temp, no_symlinks)) | |
287 | { | |
288 | printf ("%s\n", temp); | |
289 | return (bindpwd (no_symlinks)); | |
290 | } | |
291 | else | |
292 | FREE (temp); | |
726f6388 JA |
293 | } |
294 | ||
28ef6c31 JA |
295 | builtin_error ("%s: %s", dirname, strerror (errno)); |
296 | return (EXECUTION_FAILURE); | |
726f6388 | 297 | } |
726f6388 | 298 | |
ccc6cda3 JA |
299 | $BUILTIN pwd |
300 | $FUNCTION pwd_builtin | |
301 | $SHORT_DOC pwd [-PL] | |
302 | Print the current working directory. With the -P option, pwd prints | |
303 | the physical directory, without any symbolic links; the -L option | |
304 | makes pwd follow symbolic links. | |
726f6388 JA |
305 | $END |
306 | ||
ccc6cda3 JA |
307 | /* Non-zero means that pwd always prints the physical directory, without |
308 | symbolic links. */ | |
309 | static int verbatim_pwd; | |
310 | ||
311 | /* Print the name of the current working directory. */ | |
312 | int | |
313 | pwd_builtin (list) | |
726f6388 JA |
314 | WORD_LIST *list; |
315 | { | |
28ef6c31 | 316 | char *directory; |
ccc6cda3 | 317 | int opt; |
726f6388 | 318 | |
ccc6cda3 JA |
319 | verbatim_pwd = no_symbolic_links; |
320 | reset_internal_getopt (); | |
321 | while ((opt = internal_getopt (list, "LP")) != -1) | |
726f6388 | 322 | { |
ccc6cda3 | 323 | switch (opt) |
726f6388 | 324 | { |
ccc6cda3 JA |
325 | case 'P': |
326 | verbatim_pwd = 1; | |
327 | break; | |
328 | case 'L': | |
329 | verbatim_pwd = 0; | |
330 | break; | |
331 | default: | |
332 | builtin_usage (); | |
726f6388 JA |
333 | return (EXECUTION_FAILURE); |
334 | } | |
335 | } | |
ccc6cda3 | 336 | list = loptend; |
726f6388 | 337 | |
28ef6c31 | 338 | #define tcwd the_current_working_directory |
726f6388 | 339 | |
28ef6c31 JA |
340 | directory = tcwd ? (verbatim_pwd ? sh_physpath (tcwd, 0) : tcwd) |
341 | : get_working_directory ("pwd"); | |
b80f6443 JA |
342 | |
343 | /* Try again using getcwd() if canonicalization fails (for instance, if | |
344 | the file system has changed state underneath bash). */ | |
345 | if (tcwd && directory == 0) | |
346 | directory = resetpwd ("pwd"); | |
347 | ||
28ef6c31 | 348 | #undef tcwd |
726f6388 | 349 | |
ccc6cda3 JA |
350 | if (directory) |
351 | { | |
352 | printf ("%s\n", directory); | |
28ef6c31 JA |
353 | if (directory != the_current_working_directory) |
354 | free (directory); | |
ccc6cda3 | 355 | fflush (stdout); |
28ef6c31 JA |
356 | if (ferror (stdout)) |
357 | { | |
b80f6443 JA |
358 | builtin_error (_("write error: %s"), strerror (errno)); |
359 | clearerr (stdout); | |
28ef6c31 JA |
360 | return (EXECUTION_FAILURE); |
361 | } | |
b80f6443 | 362 | |
ccc6cda3 | 363 | return (EXECUTION_SUCCESS); |
726f6388 | 364 | } |
ccc6cda3 JA |
365 | else |
366 | return (EXECUTION_FAILURE); | |
726f6388 | 367 | } |
726f6388 JA |
368 | |
369 | /* Do the work of changing to the directory NEWDIR. Handle symbolic | |
28ef6c31 JA |
370 | link following, etc. This function *must* return with |
371 | the_current_working_directory either set to NULL (in which case | |
372 | getcwd() will eventually be called), or set to a string corresponding | |
373 | to the working directory. Return 1 on success, 0 on failure. */ | |
726f6388 JA |
374 | |
375 | static int | |
ccc6cda3 | 376 | change_to_directory (newdir, nolinks) |
726f6388 | 377 | char *newdir; |
ccc6cda3 | 378 | int nolinks; |
726f6388 | 379 | { |
28ef6c31 | 380 | char *t, *tdir; |
b80f6443 | 381 | int err, canon_failed, r; |
726f6388 | 382 | |
28ef6c31 | 383 | tdir = (char *)NULL; |
726f6388 | 384 | |
28ef6c31 JA |
385 | if (the_current_working_directory == 0) |
386 | { | |
387 | t = get_working_directory ("chdir"); | |
388 | FREE (t); | |
389 | } | |
726f6388 | 390 | |
28ef6c31 | 391 | t = make_absolute (newdir, the_current_working_directory); |
726f6388 | 392 | |
28ef6c31 JA |
393 | /* TDIR is either the canonicalized absolute pathname of NEWDIR |
394 | (nolinks == 0) or the absolute physical pathname of NEWDIR | |
395 | (nolinks != 0). */ | |
396 | tdir = nolinks ? sh_physpath (t, 0) | |
397 | : sh_canonpath (t, PATH_CHECKDOTDOT|PATH_CHECKEXISTS); | |
726f6388 | 398 | |
28ef6c31 JA |
399 | /* Use the canonicalized version of NEWDIR, or, if canonicalization |
400 | failed, use the non-canonical form. */ | |
7117c2d2 | 401 | canon_failed = 0; |
28ef6c31 JA |
402 | if (tdir && *tdir) |
403 | free (t); | |
404 | else | |
405 | { | |
406 | FREE (tdir); | |
407 | tdir = t; | |
7117c2d2 JA |
408 | canon_failed = 1; |
409 | } | |
410 | ||
411 | /* In POSIX mode, if we're resolving symlinks logically and sh_canonpath | |
412 | returns NULL (because it checks the path, it will return NULL if the | |
413 | resolved path doesn't exist), fail immediately. */ | |
414 | if (posixly_correct && nolinks == 0 && canon_failed) | |
415 | { | |
b80f6443 JA |
416 | #if defined ENAMETOOLONG |
417 | if (errno != ENOENT && errno != ENAMETOOLONG) | |
418 | #else | |
419 | if (errno != ENOENT) | |
420 | #endif | |
421 | errno = ENOTDIR; | |
7117c2d2 | 422 | return (0); |
28ef6c31 | 423 | } |
726f6388 | 424 | |
28ef6c31 JA |
425 | /* If the chdir succeeds, update the_current_working_directory. */ |
426 | if (chdir (nolinks ? newdir : tdir) == 0) | |
427 | { | |
7117c2d2 JA |
428 | /* If canonicalization failed, but the chdir succeeded, reset the |
429 | shell's idea of the_current_working_directory. */ | |
430 | if (canon_failed) | |
7117c2d2 | 431 | { |
b80f6443 JA |
432 | t = resetpwd ("cd"); |
433 | if (t == 0) | |
434 | set_working_directory (tdir); | |
7117c2d2 | 435 | } |
b80f6443 JA |
436 | else |
437 | set_working_directory (tdir); | |
7117c2d2 | 438 | |
28ef6c31 JA |
439 | return (1); |
440 | } | |
726f6388 | 441 | |
28ef6c31 JA |
442 | /* We failed to change to the appropriate directory name. If we tried |
443 | what the user passed (nolinks != 0), punt now. */ | |
444 | if (nolinks) | |
445 | return (0); | |
726f6388 | 446 | |
28ef6c31 | 447 | err = errno; |
726f6388 | 448 | |
28ef6c31 JA |
449 | /* We're not in physical mode (nolinks == 0), but we failed to change to |
450 | the canonicalized directory name (TDIR). Try what the user passed | |
451 | verbatim. If we succeed, reinitialize the_current_working_directory. */ | |
452 | if (chdir (newdir) == 0) | |
453 | { | |
b80f6443 JA |
454 | t = resetpwd ("cd"); |
455 | if (t == 0) | |
456 | set_working_directory (tdir); | |
457 | else | |
458 | free (t); | |
726f6388 | 459 | |
b80f6443 | 460 | r = 1; |
726f6388 JA |
461 | } |
462 | else | |
28ef6c31 JA |
463 | { |
464 | errno = err; | |
b80f6443 | 465 | r = 0; |
28ef6c31 | 466 | } |
b80f6443 JA |
467 | |
468 | free (tdir); | |
469 | return r; | |
ccc6cda3 JA |
470 | } |
471 | ||
472 | /* Code for cd spelling correction. Original patch submitted by | |
473 | Neil Russel (caret@c-side.com). */ | |
474 | ||
475 | static char * | |
476 | cdspell (dirname) | |
477 | char *dirname; | |
478 | { | |
479 | int n; | |
480 | char *guess; | |
481 | ||
482 | n = (strlen (dirname) * 3 + 1) / 2 + 1; | |
f73dda09 | 483 | guess = (char *)xmalloc (n); |
ccc6cda3 JA |
484 | |
485 | switch (spname (dirname, guess)) | |
726f6388 | 486 | { |
ccc6cda3 JA |
487 | case -1: |
488 | default: | |
489 | free (guess); | |
490 | return (char *)NULL; | |
491 | case 0: | |
492 | case 1: | |
493 | return guess; | |
494 | } | |
495 | } |