source: vendor/bash/3.1/builtins/cd.def

Last change on this file was 3228, checked in by bird, 18 years ago

bash 3.1

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