]> git.ipfire.org Git - thirdparty/bash.git/blame - builtins/cd.def
Imported from ../bash-2.05.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
JA
3
4Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
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"
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)
53extern int errno;
54#endif /* !errno */
55
ccc6cda3 56extern int posixly_correct, interactive;
d166f048 57extern int array_needs_making;
ccc6cda3
JA
58extern char *bash_getcwd_errstr;
59
60static int change_to_directory ();
61
62static char *cdspell ();
d166f048
JA
63
64/* Change this to 1 to get cd spelling correction by default. */
65int cdspelling = 0;
ccc6cda3
JA
66
67int cdable_vars;
726f6388
JA
68
69$BUILTIN cd
70$FUNCTION cd_builtin
ccc6cda3 71$SHORT_DOC cd [-PL] [dir]
726f6388 72Change the current directory to DIR. The variable $HOME is the
cce855bc 73default DIR. The variable CDPATH defines the search path for
ccc6cda3
JA
74the directory containing DIR. Alternative directory names in CDPATH
75are separated by a colon (:). A null directory name is the same as
726f6388 76the current directory, i.e. `.'. If DIR begins with a slash (/),
cce855bc 77then CDPATH is not used. If the directory is not found, and the
ccc6cda3 78shell option `cdable_vars' is set, then try the word as a variable
726f6388 79name. If that variable has a value, then cd to the value of that
ccc6cda3
JA
80variable. The -P option says to use the physical directory structure
81instead of following symbolic links; the -L option forces symbolic links
82to be followed.
726f6388
JA
83$END
84
ccc6cda3
JA
85static int
86bindpwd (no_symlinks)
87 int no_symlinks;
88{
d166f048
JA
89 char *dirname, *pwdvar;
90 int old_symlinks, old_anm;
91 SHELL_VAR *tvar;
ccc6cda3 92
28ef6c31
JA
93#define tcwd the_current_working_directory
94 dirname = tcwd ? (no_symlinks ? sh_physpath (tcwd, 0) : tcwd)
95 : get_working_directory ("cd");
96#undef tcwd
ccc6cda3 97
d166f048 98 old_anm = array_needs_making;
b72432fd
JA
99 pwdvar = get_string_value ("PWD");
100
101 tvar = bind_variable ("OLDPWD", pwdvar);
102 if (old_anm == 0 && array_needs_making && exported_p (tvar))
103 {
104 update_export_env_inplace ("OLDPWD=", 7, pwdvar);
105 array_needs_making = 0;
106 }
107
d166f048 108 tvar = bind_variable ("PWD", dirname);
d166f048
JA
109 if (old_anm == 0 && array_needs_making && exported_p (tvar))
110 {
b72432fd 111 update_export_env_inplace ("PWD=", 4, dirname);
d166f048
JA
112 array_needs_making = 0;
113 }
ccc6cda3 114
28ef6c31
JA
115 if (dirname && dirname != the_current_working_directory)
116 free (dirname);
ccc6cda3
JA
117 return (EXECUTION_SUCCESS);
118}
119
28ef6c31
JA
120#define LCD_DOVARS 0x001
121#define LCD_DOSPELL 0x002
122#define LCD_PRINTPATH 0x004
123#define LCD_FREEDIRNAME 0x010
124
726f6388
JA
125/* This builtin is ultimately the way that all user-visible commands should
126 change the current working directory. It is called by cd_to_string (),
127 so the programming interface is simple, and it handles errors and
128 restrictions properly. */
129int
130cd_builtin (list)
131 WORD_LIST *list;
132{
ccc6cda3 133 char *dirname, *cdpath, *path, *temp;
28ef6c31 134 int path_index, no_symlinks, opt, lflag;
726f6388
JA
135
136#if defined (RESTRICTED_SHELL)
137 if (restricted)
138 {
139 builtin_error ("restricted");
140 return (EXECUTION_FAILURE);
141 }
142#endif /* RESTRICTED_SHELL */
143
ccc6cda3
JA
144 no_symlinks = no_symbolic_links;
145 reset_internal_getopt ();
146 while ((opt = internal_getopt (list, "LP")) != -1)
726f6388 147 {
ccc6cda3 148 switch (opt)
726f6388 149 {
ccc6cda3
JA
150 case 'P':
151 no_symlinks = 1;
152 break;
153 case 'L':
154 no_symlinks = 0;
155 break;
156 default:
157 builtin_usage ();
726f6388
JA
158 return (EXECUTION_FAILURE);
159 }
726f6388 160 }
ccc6cda3
JA
161 list = loptend;
162
28ef6c31
JA
163 lflag = (cdable_vars ? LCD_DOVARS : 0) |
164 ((interactive && cdspelling) ? LCD_DOSPELL : 0);
165
ccc6cda3 166 if (list == 0)
726f6388 167 {
ccc6cda3 168 /* `cd' without arguments is equivalent to `cd $HOME' */
726f6388
JA
169 dirname = get_string_value ("HOME");
170
ccc6cda3 171 if (dirname == 0)
726f6388 172 {
ccc6cda3 173 builtin_error ("HOME not set");
726f6388
JA
174 return (EXECUTION_FAILURE);
175 }
28ef6c31 176 lflag = 0;
726f6388 177 }
ccc6cda3 178 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
726f6388 179 {
ccc6cda3
JA
180 /* This is `cd -', equivalent to `cd $OLDPWD' */
181 dirname = get_string_value ("OLDPWD");
726f6388 182
28ef6c31 183 if (dirname == 0)
726f6388 184 {
28ef6c31 185 builtin_error ("OLDPWD not set");
726f6388
JA
186 return (EXECUTION_FAILURE);
187 }
28ef6c31 188 lflag = interactive ? LCD_PRINTPATH : 0;
726f6388 189 }
28ef6c31
JA
190 else if (absolute_pathname (list->word->word))
191 dirname = list->word->word;
192 else if (cdpath = get_string_value ("CDPATH"))
726f6388 193 {
ccc6cda3
JA
194 dirname = list->word->word;
195
28ef6c31
JA
196 /* Find directory in $CDPATH. */
197 path_index = 0;
198 while (path = extract_colon_unit (cdpath, &path_index))
726f6388 199 {
28ef6c31
JA
200 /* OPT is 1 if the path element is non-empty */
201 opt = path[0] != '\0';
202 temp = sh_makepath (path, dirname, MP_DOTILDE);
203 free (path);
cce855bc 204
28ef6c31 205 if (change_to_directory (temp, no_symlinks))
cce855bc 206 {
28ef6c31
JA
207 /* POSIX.2 says that if a nonempty directory from CDPATH
208 is used to find the directory to change to, the new
209 directory name is echoed to stdout, whether or not
210 the shell is interactive. */
211 if (opt)
212 printf ("%s\n", no_symlinks ? temp : the_current_working_directory);
213
214 free (temp);
215 /* Posix.2 says that after using CDPATH, the resultant
216 value of $PWD will not contain `.' or `..'. */
217 return (bindpwd (posixly_correct || no_symlinks));
cce855bc 218 }
28ef6c31
JA
219 else
220 free (temp);
726f6388
JA
221 }
222
28ef6c31
JA
223 /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't
224 try the current directory, so we just punt now with an error
225 message if POSIXLY_CORRECT is non-zero. The check for cdpath[0]
226 is so we don't mistakenly treat a CDPATH value of "" as not
227 specifying the current directory. */
228 if (posixly_correct && cdpath[0])
726f6388 229 {
28ef6c31
JA
230 builtin_error ("%s: %s", dirname, strerror (ENOENT));
231 return (EXECUTION_FAILURE);
726f6388 232 }
28ef6c31
JA
233 }
234 else
235 dirname = list->word->word;
236
237 /* When we get here, DIRNAME is the directory to change to. If we
238 chdir successfully, just return. */
239 if (change_to_directory (dirname, no_symlinks))
240 {
241 if (lflag & LCD_PRINTPATH)
242 printf ("%s\n", dirname);
243 return (bindpwd (no_symlinks));
244 }
726f6388 245
28ef6c31
JA
246 /* If the user requests it, then perhaps this is the name of
247 a shell variable, whose value contains the directory to
248 change to. */
249 if (lflag & LCD_DOVARS)
250 {
251 temp = get_string_value (dirname);
252 if (temp && change_to_directory (temp, no_symlinks))
726f6388 253 {
28ef6c31
JA
254 printf ("%s\n", temp);
255 return (bindpwd (no_symlinks));
726f6388 256 }
28ef6c31 257 }
726f6388 258
28ef6c31
JA
259 /* If the user requests it, try to find a directory name similar in
260 spelling to the one requested, in case the user made a simple
261 typo. This is similar to the UNIX 8th and 9th Edition shells. */
262 if (lflag & LCD_DOSPELL)
263 {
264 temp = cdspell (dirname);
265 if (temp && change_to_directory (temp, no_symlinks))
266 {
267 printf ("%s\n", temp);
268 return (bindpwd (no_symlinks));
269 }
270 else
271 FREE (temp);
726f6388
JA
272 }
273
28ef6c31
JA
274 builtin_error ("%s: %s", dirname, strerror (errno));
275 return (EXECUTION_FAILURE);
726f6388 276}
726f6388 277
ccc6cda3
JA
278$BUILTIN pwd
279$FUNCTION pwd_builtin
280$SHORT_DOC pwd [-PL]
281Print the current working directory. With the -P option, pwd prints
282the physical directory, without any symbolic links; the -L option
283makes pwd follow symbolic links.
726f6388
JA
284$END
285
ccc6cda3
JA
286/* Non-zero means that pwd always prints the physical directory, without
287 symbolic links. */
288static int verbatim_pwd;
289
290/* Print the name of the current working directory. */
291int
292pwd_builtin (list)
726f6388
JA
293 WORD_LIST *list;
294{
28ef6c31 295 char *directory;
ccc6cda3 296 int opt;
726f6388 297
ccc6cda3
JA
298 verbatim_pwd = no_symbolic_links;
299 reset_internal_getopt ();
300 while ((opt = internal_getopt (list, "LP")) != -1)
726f6388 301 {
ccc6cda3 302 switch (opt)
726f6388 303 {
ccc6cda3
JA
304 case 'P':
305 verbatim_pwd = 1;
306 break;
307 case 'L':
308 verbatim_pwd = 0;
309 break;
310 default:
311 builtin_usage ();
726f6388
JA
312 return (EXECUTION_FAILURE);
313 }
314 }
ccc6cda3 315 list = loptend;
726f6388 316
28ef6c31 317#define tcwd the_current_working_directory
726f6388 318
28ef6c31
JA
319 directory = tcwd ? (verbatim_pwd ? sh_physpath (tcwd, 0) : tcwd)
320 : get_working_directory ("pwd");
321#undef tcwd
726f6388 322
ccc6cda3
JA
323 if (directory)
324 {
325 printf ("%s\n", directory);
28ef6c31
JA
326 if (directory != the_current_working_directory)
327 free (directory);
ccc6cda3 328 fflush (stdout);
28ef6c31
JA
329 if (ferror (stdout))
330 {
331 builtin_error ("write error: %s", strerror (errno));
332 return (EXECUTION_FAILURE);
333 }
ccc6cda3 334 return (EXECUTION_SUCCESS);
726f6388 335 }
ccc6cda3
JA
336 else
337 return (EXECUTION_FAILURE);
726f6388 338}
726f6388
JA
339
340/* Do the work of changing to the directory NEWDIR. Handle symbolic
28ef6c31
JA
341 link following, etc. This function *must* return with
342 the_current_working_directory either set to NULL (in which case
343 getcwd() will eventually be called), or set to a string corresponding
344 to the working directory. Return 1 on success, 0 on failure. */
726f6388
JA
345
346static int
ccc6cda3 347change_to_directory (newdir, nolinks)
726f6388 348 char *newdir;
ccc6cda3 349 int nolinks;
726f6388 350{
28ef6c31
JA
351 char *t, *tdir;
352 int err;
726f6388 353
28ef6c31 354 tdir = (char *)NULL;
726f6388 355
28ef6c31
JA
356 if (the_current_working_directory == 0)
357 {
358 t = get_working_directory ("chdir");
359 FREE (t);
360 }
726f6388 361
28ef6c31 362 t = make_absolute (newdir, the_current_working_directory);
726f6388 363
28ef6c31
JA
364 /* TDIR is either the canonicalized absolute pathname of NEWDIR
365 (nolinks == 0) or the absolute physical pathname of NEWDIR
366 (nolinks != 0). */
367 tdir = nolinks ? sh_physpath (t, 0)
368 : sh_canonpath (t, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
726f6388 369
28ef6c31
JA
370 /* Use the canonicalized version of NEWDIR, or, if canonicalization
371 failed, use the non-canonical form. */
372 if (tdir && *tdir)
373 free (t);
374 else
375 {
376 FREE (tdir);
377 tdir = t;
378 }
726f6388 379
28ef6c31
JA
380 /* If the chdir succeeds, update the_current_working_directory. */
381 if (chdir (nolinks ? newdir : tdir) == 0)
382 {
383 FREE (the_current_working_directory);
384 the_current_working_directory = tdir;
385
386 return (1);
387 }
726f6388 388
28ef6c31
JA
389 /* We failed to change to the appropriate directory name. If we tried
390 what the user passed (nolinks != 0), punt now. */
391 if (nolinks)
392 return (0);
726f6388 393
28ef6c31
JA
394 err = errno;
395 free (tdir);
726f6388 396
28ef6c31
JA
397 /* We're not in physical mode (nolinks == 0), but we failed to change to
398 the canonicalized directory name (TDIR). Try what the user passed
399 verbatim. If we succeed, reinitialize the_current_working_directory. */
400 if (chdir (newdir) == 0)
401 {
402 FREE (the_current_working_directory);
403 the_current_working_directory = (char *)NULL;
404 tdir = get_working_directory ("cd");
405 FREE (tdir);
726f6388 406
28ef6c31 407 return (1);
726f6388
JA
408 }
409 else
28ef6c31
JA
410 {
411 errno = err;
412 return (0);
413 }
ccc6cda3
JA
414}
415
416/* Code for cd spelling correction. Original patch submitted by
417 Neil Russel (caret@c-side.com). */
418
419static char *
420cdspell (dirname)
421 char *dirname;
422{
423 int n;
424 char *guess;
425
426 n = (strlen (dirname) * 3 + 1) / 2 + 1;
427 guess = xmalloc (n);
428
429 switch (spname (dirname, guess))
726f6388 430 {
ccc6cda3
JA
431 case -1:
432 default:
433 free (guess);
434 return (char *)NULL;
435 case 0:
436 case 1:
437 return guess;
438 }
439}