]>
Commit | Line | Data |
---|---|---|
726f6388 | 1 | This file is cd.def, from which is created cd.c. It implements the |
ccc6cda3 | 2 | builtins "cd" and "pwd" in Bash. |
726f6388 JA |
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 | |
bb70624e | 10 | Software Foundation; either version 2, or (at your option) any later |
726f6388 JA |
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 | |
bb70624e | 20 | Foundation, 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) | |
53 | extern int errno; | |
54 | #endif /* !errno */ | |
55 | ||
ccc6cda3 | 56 | extern int posixly_correct, interactive; |
d166f048 | 57 | extern int array_needs_making; |
ccc6cda3 JA |
58 | extern char *bash_getcwd_errstr; |
59 | ||
60 | static int change_to_directory (); | |
61 | ||
62 | static char *cdspell (); | |
d166f048 JA |
63 | |
64 | /* Change this to 1 to get cd spelling correction by default. */ | |
65 | int cdspelling = 0; | |
ccc6cda3 JA |
66 | |
67 | int cdable_vars; | |
726f6388 JA |
68 | |
69 | $BUILTIN cd | |
70 | $FUNCTION cd_builtin | |
ccc6cda3 | 71 | $SHORT_DOC cd [-PL] [dir] |
726f6388 | 72 | Change the current directory to DIR. The variable $HOME is the |
cce855bc | 73 | default DIR. The variable CDPATH defines the search path for |
ccc6cda3 JA |
74 | the directory containing DIR. Alternative directory names in CDPATH |
75 | are separated by a colon (:). A null directory name is the same as | |
726f6388 | 76 | the current directory, i.e. `.'. If DIR begins with a slash (/), |
cce855bc | 77 | then CDPATH is not used. If the directory is not found, and the |
ccc6cda3 | 78 | shell option `cdable_vars' is set, then try the word as a variable |
726f6388 | 79 | name. If that variable has a value, then cd to the value of that |
ccc6cda3 JA |
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. | |
726f6388 JA |
83 | $END |
84 | ||
ccc6cda3 JA |
85 | static int |
86 | bindpwd (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. */ | |
129 | int | |
130 | cd_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] | |
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. | |
726f6388 JA |
284 | $END |
285 | ||
ccc6cda3 JA |
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) | |
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 | |
346 | static int | |
ccc6cda3 | 347 | change_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 | ||
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)) | |
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 | } |