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