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