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