]> git.ipfire.org Git - thirdparty/bash.git/blob - builtins/cd.def
Imported from ../bash-2.05.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, 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, interactive;
57 extern int array_needs_making;
58 extern char *bash_getcwd_errstr;
59
60 static int change_to_directory ();
61
62 static char *cdspell ();
63
64 /* Change this to 1 to get cd spelling correction by default. */
65 int cdspelling = 0;
66
67 int cdable_vars;
68
69 $BUILTIN cd
70 $FUNCTION cd_builtin
71 $SHORT_DOC cd [-PL] [dir]
72 Change the current directory to DIR. The variable $HOME is the
73 default DIR. The variable CDPATH defines the search path for
74 the directory containing DIR. Alternative directory names in CDPATH
75 are separated by a colon (:). A null directory name is the same as
76 the current directory, i.e. `.'. If DIR begins with a slash (/),
77 then CDPATH is not used. If the directory is not found, and the
78 shell option `cdable_vars' is set, then try the word as a variable
79 name. If that variable has a value, then cd to the value of that
80 variable. The -P option says to use the physical directory structure
81 instead of following symbolic links; the -L option forces symbolic links
82 to be followed.
83 $END
84
85 static int
86 bindpwd (no_symlinks)
87 int no_symlinks;
88 {
89 char *dirname, *pwdvar;
90 int old_symlinks, old_anm;
91 SHELL_VAR *tvar;
92
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
97
98 old_anm = array_needs_making;
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
108 tvar = bind_variable ("PWD", dirname);
109 if (old_anm == 0 && array_needs_making && exported_p (tvar))
110 {
111 update_export_env_inplace ("PWD=", 4, dirname);
112 array_needs_making = 0;
113 }
114
115 if (dirname && dirname != the_current_working_directory)
116 free (dirname);
117 return (EXECUTION_SUCCESS);
118 }
119
120 #define LCD_DOVARS 0x001
121 #define LCD_DOSPELL 0x002
122 #define LCD_PRINTPATH 0x004
123 #define LCD_FREEDIRNAME 0x010
124
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. */
129 int
130 cd_builtin (list)
131 WORD_LIST *list;
132 {
133 char *dirname, *cdpath, *path, *temp;
134 int path_index, no_symlinks, opt, lflag;
135
136 #if defined (RESTRICTED_SHELL)
137 if (restricted)
138 {
139 builtin_error ("restricted");
140 return (EXECUTION_FAILURE);
141 }
142 #endif /* RESTRICTED_SHELL */
143
144 no_symlinks = no_symbolic_links;
145 reset_internal_getopt ();
146 while ((opt = internal_getopt (list, "LP")) != -1)
147 {
148 switch (opt)
149 {
150 case 'P':
151 no_symlinks = 1;
152 break;
153 case 'L':
154 no_symlinks = 0;
155 break;
156 default:
157 builtin_usage ();
158 return (EXECUTION_FAILURE);
159 }
160 }
161 list = loptend;
162
163 lflag = (cdable_vars ? LCD_DOVARS : 0) |
164 ((interactive && cdspelling) ? LCD_DOSPELL : 0);
165
166 if (list == 0)
167 {
168 /* `cd' without arguments is equivalent to `cd $HOME' */
169 dirname = get_string_value ("HOME");
170
171 if (dirname == 0)
172 {
173 builtin_error ("HOME not set");
174 return (EXECUTION_FAILURE);
175 }
176 lflag = 0;
177 }
178 else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
179 {
180 /* This is `cd -', equivalent to `cd $OLDPWD' */
181 dirname = get_string_value ("OLDPWD");
182
183 if (dirname == 0)
184 {
185 builtin_error ("OLDPWD not set");
186 return (EXECUTION_FAILURE);
187 }
188 lflag = interactive ? LCD_PRINTPATH : 0;
189 }
190 else if (absolute_pathname (list->word->word))
191 dirname = list->word->word;
192 else if (cdpath = get_string_value ("CDPATH"))
193 {
194 dirname = list->word->word;
195
196 /* Find directory in $CDPATH. */
197 path_index = 0;
198 while (path = extract_colon_unit (cdpath, &path_index))
199 {
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);
204
205 if (change_to_directory (temp, no_symlinks))
206 {
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));
218 }
219 else
220 free (temp);
221 }
222
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])
229 {
230 builtin_error ("%s: %s", dirname, strerror (ENOENT));
231 return (EXECUTION_FAILURE);
232 }
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 }
245
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))
253 {
254 printf ("%s\n", temp);
255 return (bindpwd (no_symlinks));
256 }
257 }
258
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);
272 }
273
274 builtin_error ("%s: %s", dirname, strerror (errno));
275 return (EXECUTION_FAILURE);
276 }
277
278 $BUILTIN pwd
279 $FUNCTION pwd_builtin
280 $SHORT_DOC pwd [-PL]
281 Print the current working directory. With the -P option, pwd prints
282 the physical directory, without any symbolic links; the -L option
283 makes pwd follow symbolic links.
284 $END
285
286 /* Non-zero means that pwd always prints the physical directory, without
287 symbolic links. */
288 static int verbatim_pwd;
289
290 /* Print the name of the current working directory. */
291 int
292 pwd_builtin (list)
293 WORD_LIST *list;
294 {
295 char *directory;
296 int opt;
297
298 verbatim_pwd = no_symbolic_links;
299 reset_internal_getopt ();
300 while ((opt = internal_getopt (list, "LP")) != -1)
301 {
302 switch (opt)
303 {
304 case 'P':
305 verbatim_pwd = 1;
306 break;
307 case 'L':
308 verbatim_pwd = 0;
309 break;
310 default:
311 builtin_usage ();
312 return (EXECUTION_FAILURE);
313 }
314 }
315 list = loptend;
316
317 #define tcwd the_current_working_directory
318
319 directory = tcwd ? (verbatim_pwd ? sh_physpath (tcwd, 0) : tcwd)
320 : get_working_directory ("pwd");
321 #undef tcwd
322
323 if (directory)
324 {
325 printf ("%s\n", directory);
326 if (directory != the_current_working_directory)
327 free (directory);
328 fflush (stdout);
329 if (ferror (stdout))
330 {
331 builtin_error ("write error: %s", strerror (errno));
332 return (EXECUTION_FAILURE);
333 }
334 return (EXECUTION_SUCCESS);
335 }
336 else
337 return (EXECUTION_FAILURE);
338 }
339
340 /* Do the work of changing to the directory NEWDIR. Handle symbolic
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. */
345
346 static int
347 change_to_directory (newdir, nolinks)
348 char *newdir;
349 int nolinks;
350 {
351 char *t, *tdir;
352 int err;
353
354 tdir = (char *)NULL;
355
356 if (the_current_working_directory == 0)
357 {
358 t = get_working_directory ("chdir");
359 FREE (t);
360 }
361
362 t = make_absolute (newdir, the_current_working_directory);
363
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);
369
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 }
379
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 }
388
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);
393
394 err = errno;
395 free (tdir);
396
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);
406
407 return (1);
408 }
409 else
410 {
411 errno = err;
412 return (0);
413 }
414 }
415
416 /* Code for cd spelling correction. Original patch submitted by
417 Neil Russel (caret@c-side.com). */
418
419 static char *
420 cdspell (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))
430 {
431 case -1:
432 default:
433 free (guess);
434 return (char *)NULL;
435 case 0:
436 case 1:
437 return guess;
438 }
439 }