]> git.ipfire.org Git - thirdparty/bash.git/blob - builtins/cd.def
commit bash-20080814 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-2008 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 $BUILTIN cd
71 $FUNCTION cd_builtin
72 $SHORT_DOC cd [-L|-P] [dir]
73 Change the shell working directory.
74
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.
93
94 Exit Status:
95 Returns 0 if the directory is changed; non-zero otherwise.
96 $END
97
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
115 static int
116 bindpwd (no_symlinks)
117 int no_symlinks;
118 {
119 char *dirname, *pwdvar;
120 int old_anm, r;
121 SHELL_VAR *tvar;
122
123 r = sh_chkwrite (EXECUTION_SUCCESS);
124
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
129
130 old_anm = array_needs_making;
131 pwdvar = get_string_value ("PWD");
132
133 tvar = bind_variable ("OLDPWD", pwdvar, 0);
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
140 setpwd (dirname);
141
142 if (dirname && dirname != the_current_working_directory)
143 free (dirname);
144
145 return (r);
146 }
147
148 /* Call get_working_directory to reset the value of
149 the_current_working_directory () */
150 static char *
151 resetpwd (caller)
152 char *caller;
153 {
154 char *tdir;
155
156 FREE (the_current_working_directory);
157 the_current_working_directory = (char *)NULL;
158 tdir = get_working_directory (caller);
159 return (tdir);
160 }
161
162 #define LCD_DOVARS 0x001
163 #define LCD_DOSPELL 0x002
164 #define LCD_PRINTPATH 0x004
165 #define LCD_FREEDIRNAME 0x010
166
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 {
175 char *dirname, *cdpath, *path, *temp;
176 int path_index, no_symlinks, opt, lflag;
177
178 #if defined (RESTRICTED_SHELL)
179 if (restricted)
180 {
181 sh_restricted ((char *)NULL);
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 lflag = (cdable_vars ? LCD_DOVARS : 0) |
206 ((interactive && cdspelling) ? LCD_DOSPELL : 0);
207
208 if (list == 0)
209 {
210 /* `cd' without arguments is equivalent to `cd $HOME' */
211 dirname = get_string_value ("HOME");
212
213 if (dirname == 0)
214 {
215 builtin_error (_("HOME not set"));
216 return (EXECUTION_FAILURE);
217 }
218 lflag = 0;
219 }
220 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
221 {
222 /* This is `cd -', equivalent to `cd $OLDPWD' */
223 dirname = get_string_value ("OLDPWD");
224
225 if (dirname == 0)
226 {
227 builtin_error (_("OLDPWD not set"));
228 return (EXECUTION_FAILURE);
229 }
230 #if 0
231 lflag = interactive ? LCD_PRINTPATH : 0;
232 #else
233 lflag = LCD_PRINTPATH; /* According to SUSv3 */
234 #endif
235 }
236 else if (absolute_pathname (list->word->word))
237 dirname = list->word->word;
238 else if (cdpath = get_string_value ("CDPATH"))
239 {
240 dirname = list->word->word;
241
242 /* Find directory in $CDPATH. */
243 path_index = 0;
244 while (path = extract_colon_unit (cdpath, &path_index))
245 {
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);
250
251 if (change_to_directory (temp, no_symlinks))
252 {
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. */
257 if (opt && (path = no_symlinks ? temp : the_current_working_directory))
258 printf ("%s\n", path);
259
260 free (temp);
261 #if 0
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));
265 #else
266 return (bindpwd (no_symlinks));
267 #endif
268 }
269 else
270 free (temp);
271 }
272
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])
279 {
280 builtin_error ("%s: %s", dirname, strerror (ENOENT));
281 return (EXECUTION_FAILURE);
282 }
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 }
295
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))
303 {
304 printf ("%s\n", temp);
305 return (bindpwd (no_symlinks));
306 }
307 }
308
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 {
314 temp = dirspell (dirname);
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);
322 }
323
324 builtin_error ("%s: %s", dirname, strerror (errno));
325 return (EXECUTION_FAILURE);
326 }
327
328 $BUILTIN pwd
329 $FUNCTION pwd_builtin
330 $SHORT_DOC pwd [-LP]
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.
339
340 Exit Status:
341 Returns 0 unless an invalid option is given or the current directory
342 cannot be read.
343 $END
344
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)
352 WORD_LIST *list;
353 {
354 char *directory;
355 int opt, pflag;
356
357 verbatim_pwd = no_symbolic_links;
358 pflag = 0;
359 reset_internal_getopt ();
360 while ((opt = internal_getopt (list, "LP")) != -1)
361 {
362 switch (opt)
363 {
364 case 'P':
365 verbatim_pwd = pflag = 1;
366 break;
367 case 'L':
368 verbatim_pwd = 0;
369 break;
370 default:
371 builtin_usage ();
372 return (EXECUTION_FAILURE);
373 }
374 }
375 list = loptend;
376
377 #define tcwd the_current_working_directory
378
379 directory = tcwd ? (verbatim_pwd ? sh_physpath (tcwd, 0) : tcwd)
380 : get_working_directory ("pwd");
381
382 /* Try again using getcwd() if canonicalization fails (for instance, if
383 the file system has changed state underneath bash). */
384 if ((tcwd && directory == 0) ||
385 (posixly_correct && same_file (".", tcwd, (struct stat *)0, (struct stat *)0) == 0))
386 directory = resetpwd ("pwd");
387
388 #undef tcwd
389
390 if (directory)
391 {
392 printf ("%s\n", directory);
393 /* This is dumb but posix-mandated. */
394 if (posixly_correct && pflag)
395 setpwd (directory);
396 if (directory != the_current_working_directory)
397 free (directory);
398 return (sh_chkwrite (EXECUTION_SUCCESS));
399 }
400 else
401 return (EXECUTION_FAILURE);
402 }
403
404 /* Do the work of changing to the directory NEWDIR. Handle symbolic
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. */
409
410 static int
411 change_to_directory (newdir, nolinks)
412 char *newdir;
413 int nolinks;
414 {
415 char *t, *tdir;
416 int err, canon_failed, r, ndlen, dlen;
417
418 tdir = (char *)NULL;
419
420 if (the_current_working_directory == 0)
421 {
422 t = get_working_directory ("chdir");
423 FREE (t);
424 }
425
426 t = make_absolute (newdir, the_current_working_directory);
427
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);
433
434 ndlen = strlen (newdir);
435 dlen = strlen (t);
436
437 /* Use the canonicalized version of NEWDIR, or, if canonicalization
438 failed, use the non-canonical form. */
439 canon_failed = 0;
440 if (tdir && *tdir)
441 free (t);
442 else
443 {
444 FREE (tdir);
445 tdir = t;
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. */
452 if (posixly_correct && nolinks == 0 && canon_failed && (errno != ENAMETOOLONG || ndlen > PATH_MAX))
453 {
454 #if defined ENAMETOOLONG
455 if (errno != ENOENT && errno != ENAMETOOLONG)
456 #else
457 if (errno != ENOENT)
458 #endif
459 errno = ENOTDIR;
460 free (tdir);
461 return (0);
462 }
463
464 /* If the chdir succeeds, update the_current_working_directory. */
465 if (chdir (nolinks ? newdir : tdir) == 0)
466 {
467 /* If canonicalization failed, but the chdir succeeded, reset the
468 shell's idea of the_current_working_directory. */
469 if (canon_failed)
470 {
471 t = resetpwd ("cd");
472 if (t == 0)
473 set_working_directory (tdir);
474 }
475 else
476 set_working_directory (tdir);
477
478 free (tdir);
479 return (1);
480 }
481
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)
485 {
486 free (tdir);
487 return (0);
488 }
489
490 err = errno;
491
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 {
497 t = resetpwd ("cd");
498 if (t == 0)
499 set_working_directory (tdir);
500 else
501 free (t);
502
503 r = 1;
504 }
505 else
506 {
507 errno = err;
508 r = 0;
509 }
510
511 free (tdir);
512 return r;
513 }