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