]> git.ipfire.org Git - thirdparty/bash.git/blame - builtins/cd.def
Imported from ../bash-3.0.16.tar.gz.
[thirdparty/bash.git] / builtins / cd.def
CommitLineData
726f6388 1This file is cd.def, from which is created cd.c. It implements the
ccc6cda3 2builtins "cd" and "pwd" in Bash.
726f6388 3
b80f6443 4Copyright (C) 1987-2003 Free Software Foundation, Inc.
726f6388
JA
5
6This file is part of GNU Bash, the Bourne Again SHell.
7
8Bash is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
bb70624e 10Software Foundation; either version 2, or (at your option) any later
726f6388
JA
11version.
12
13Bash is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16for more details.
17
18You should have received a copy of the GNU General Public License along
19with Bash; see the file COPYING. If not, write to the Free Software
bb70624e 20Foundation, 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)
54extern int errno;
55#endif /* !errno */
56
f73dda09 57extern int posixly_correct;
d166f048 58extern int array_needs_making;
ccc6cda3
JA
59extern char *bash_getcwd_errstr;
60
f73dda09
JA
61static int bindpwd __P((int));
62static int change_to_directory __P((char *, int));
ccc6cda3 63
f73dda09 64static char *cdspell __P((char *));
d166f048
JA
65
66/* Change this to 1 to get cd spelling correction by default. */
67int cdspelling = 0;
ccc6cda3
JA
68
69int cdable_vars;
726f6388
JA
70
71$BUILTIN cd
72$FUNCTION cd_builtin
7117c2d2 73$SHORT_DOC cd [-L|-P] [dir]
726f6388 74Change the current directory to DIR. The variable $HOME is the
cce855bc 75default DIR. The variable CDPATH defines the search path for
ccc6cda3
JA
76the directory containing DIR. Alternative directory names in CDPATH
77are separated by a colon (:). A null directory name is the same as
726f6388 78the current directory, i.e. `.'. If DIR begins with a slash (/),
cce855bc 79then CDPATH is not used. If the directory is not found, and the
ccc6cda3 80shell option `cdable_vars' is set, then try the word as a variable
726f6388 81name. If that variable has a value, then cd to the value of that
ccc6cda3
JA
82variable. The -P option says to use the physical directory structure
83instead of following symbolic links; the -L option forces symbolic links
84to be followed.
726f6388
JA
85$END
86
ccc6cda3
JA
87static int
88bindpwd (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 () */
125static char *
b80f6443
JA
126resetpwd (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. */
146int
147cd_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]
302Print the current working directory. With the -P option, pwd prints
303the physical directory, without any symbolic links; the -L option
304makes 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. */
309static int verbatim_pwd;
310
311/* Print the name of the current working directory. */
312int
313pwd_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
375static int
ccc6cda3 376change_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
475static char *
476cdspell (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}