]>
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 | 3 | |
a0c0a00f | 4 | Copyright (C) 1987-2016 Free Software Foundation, Inc. |
726f6388 JA |
5 | |
6 | This file is part of GNU Bash, the Bourne Again SHell. | |
7 | ||
3185942a JA |
8 | Bash is free software: you can redistribute it and/or modify |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation, either version 3 of the License, or | |
11 | (at your option) any later version. | |
726f6388 | 12 | |
3185942a JA |
13 | Bash is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
726f6388 | 17 | |
3185942a JA |
18 | You should have received a copy of the GNU General Public License |
19 | along with Bash. If not, see <http://www.gnu.org/licenses/>. | |
726f6388 JA |
20 | |
21 | $PRODUCES cd.c | |
ccc6cda3 | 22 | #include <config.h> |
726f6388 | 23 | |
ccc6cda3 | 24 | #if defined (HAVE_UNISTD_H) |
cce855bc JA |
25 | # ifdef _MINIX |
26 | # include <sys/types.h> | |
27 | # endif | |
ccc6cda3 JA |
28 | # include <unistd.h> |
29 | #endif | |
30 | ||
31 | #include "../bashtypes.h" | |
bb70624e JA |
32 | #include "posixdir.h" |
33 | #include "posixstat.h" | |
ac50fbac | 34 | #if defined (HAVE_SYS_PARAM_H) |
726f6388 | 35 | #include <sys/param.h> |
cce855bc | 36 | #endif |
ac50fbac | 37 | #include <fcntl.h> |
726f6388 | 38 | |
ccc6cda3 JA |
39 | #include <stdio.h> |
40 | ||
41 | #include "../bashansi.h" | |
b80f6443 | 42 | #include "../bashintl.h" |
726f6388 JA |
43 | |
44 | #include <errno.h> | |
45 | #include <tilde/tilde.h> | |
46 | ||
47 | #include "../shell.h" | |
48 | #include "../flags.h" | |
bb70624e | 49 | #include "maxpath.h" |
726f6388 | 50 | #include "common.h" |
ccc6cda3 | 51 | #include "bashgetopt.h" |
726f6388 JA |
52 | |
53 | #if !defined (errno) | |
54 | extern int errno; | |
55 | #endif /* !errno */ | |
56 | ||
3185942a | 57 | extern const char * const bash_getcwd_errstr; |
ccc6cda3 | 58 | |
f73dda09 | 59 | static int bindpwd __P((int)); |
495aee44 | 60 | static int setpwd __P((char *)); |
0628567a | 61 | static char *resetpwd __P((char *)); |
ac50fbac CR |
62 | static int change_to_directory __P((char *, int, int)); |
63 | ||
64 | static int cdxattr __P((char *, char **)); | |
65 | static void resetxattr __P((void)); | |
ccc6cda3 | 66 | |
d166f048 JA |
67 | /* Change this to 1 to get cd spelling correction by default. */ |
68 | int cdspelling = 0; | |
ccc6cda3 JA |
69 | |
70 | int cdable_vars; | |
726f6388 | 71 | |
495aee44 | 72 | static int eflag; /* file scope so bindpwd() can see it */ |
ac50fbac CR |
73 | static int xattrflag; /* O_XATTR support for openat */ |
74 | static int xattrfd = -1; | |
495aee44 | 75 | |
726f6388 JA |
76 | $BUILTIN cd |
77 | $FUNCTION cd_builtin | |
ac50fbac | 78 | $SHORT_DOC cd [-L|[-P [-e]] [-@]] [dir] |
3185942a JA |
79 | Change the shell working directory. |
80 | ||
81 | Change the current directory to DIR. The default DIR is the value of the | |
82 | HOME shell variable. | |
83 | ||
84 | The variable CDPATH defines the search path for the directory containing | |
85 | DIR. Alternative directory names in CDPATH are separated by a colon (:). | |
86 | A null directory name is the same as the current directory. If DIR begins | |
87 | with a slash (/), then CDPATH is not used. | |
88 | ||
89 | If the directory is not found, and the shell option `cdable_vars' is set, | |
90 | the word is assumed to be a variable name. If that variable has a value, | |
91 | its value is used for DIR. | |
92 | ||
93 | Options: | |
a0c0a00f CR |
94 | -L force symbolic links to be followed: resolve symbolic |
95 | links in DIR after processing instances of `..' | |
96 | -P use the physical directory structure without following | |
97 | symbolic links: resolve symbolic links in DIR before | |
98 | processing instances of `..' | |
99 | -e if the -P option is supplied, and the current working | |
100 | directory cannot be determined successfully, exit with | |
101 | a non-zero status | |
ac50fbac | 102 | #if defined (O_XATTR) |
a0c0a00f CR |
103 | -@ on systems that support it, present a file with extended |
104 | attributes as a directory containing the file attributes | |
ac50fbac | 105 | #endif |
3185942a JA |
106 | |
107 | The default is to follow symbolic links, as if `-L' were specified. | |
ac50fbac CR |
108 | `..' is processed by removing the immediately previous pathname component |
109 | back to a slash or the beginning of DIR. | |
3185942a JA |
110 | |
111 | Exit Status: | |
495aee44 CR |
112 | Returns 0 if the directory is changed, and if $PWD is set successfully when |
113 | -P is used; non-zero otherwise. | |
726f6388 JA |
114 | $END |
115 | ||
95732b49 | 116 | /* Just set $PWD, don't change OLDPWD. Used by `pwd -P' in posix mode. */ |
495aee44 | 117 | static int |
95732b49 JA |
118 | setpwd (dirname) |
119 | char *dirname; | |
120 | { | |
121 | int old_anm; | |
122 | SHELL_VAR *tvar; | |
123 | ||
124 | old_anm = array_needs_making; | |
125 | tvar = bind_variable ("PWD", dirname ? dirname : "", 0); | |
495aee44 CR |
126 | if (tvar && readonly_p (tvar)) |
127 | return EXECUTION_FAILURE; | |
128 | if (tvar && old_anm == 0 && array_needs_making && exported_p (tvar)) | |
95732b49 JA |
129 | { |
130 | update_export_env_inplace ("PWD=", 4, dirname ? dirname : ""); | |
131 | array_needs_making = 0; | |
132 | } | |
495aee44 | 133 | return EXECUTION_SUCCESS; |
95732b49 JA |
134 | } |
135 | ||
ccc6cda3 JA |
136 | static int |
137 | bindpwd (no_symlinks) | |
138 | int no_symlinks; | |
139 | { | |
d166f048 | 140 | char *dirname, *pwdvar; |
d233b485 | 141 | int old_anm, r, canon_failed; |
d166f048 | 142 | SHELL_VAR *tvar; |
ccc6cda3 | 143 | |
3185942a JA |
144 | r = sh_chkwrite (EXECUTION_SUCCESS); |
145 | ||
28ef6c31 JA |
146 | #define tcwd the_current_working_directory |
147 | dirname = tcwd ? (no_symlinks ? sh_physpath (tcwd, 0) : tcwd) | |
148 | : get_working_directory ("cd"); | |
149 | #undef tcwd | |
ccc6cda3 | 150 | |
d233b485 CR |
151 | /* If canonicalization fails, reset dirname to the_current_working_directory */ |
152 | if (dirname == 0) | |
153 | { | |
154 | canon_failed = 1; | |
155 | dirname = the_current_working_directory; | |
156 | } | |
157 | ||
d166f048 | 158 | old_anm = array_needs_making; |
b72432fd JA |
159 | pwdvar = get_string_value ("PWD"); |
160 | ||
95732b49 | 161 | tvar = bind_variable ("OLDPWD", pwdvar, 0); |
495aee44 CR |
162 | if (tvar && readonly_p (tvar)) |
163 | r = EXECUTION_FAILURE; | |
164 | ||
b72432fd JA |
165 | if (old_anm == 0 && array_needs_making && exported_p (tvar)) |
166 | { | |
167 | update_export_env_inplace ("OLDPWD=", 7, pwdvar); | |
168 | array_needs_making = 0; | |
169 | } | |
170 | ||
495aee44 CR |
171 | if (setpwd (dirname) == EXECUTION_FAILURE) |
172 | r = EXECUTION_FAILURE; | |
d233b485 | 173 | if (canon_failed && eflag) |
495aee44 | 174 | r = EXECUTION_FAILURE; |
ccc6cda3 | 175 | |
28ef6c31 JA |
176 | if (dirname && dirname != the_current_working_directory) |
177 | free (dirname); | |
b80f6443 | 178 | |
3185942a | 179 | return (r); |
ccc6cda3 JA |
180 | } |
181 | ||
7117c2d2 JA |
182 | /* Call get_working_directory to reset the value of |
183 | the_current_working_directory () */ | |
184 | static char * | |
b80f6443 JA |
185 | resetpwd (caller) |
186 | char *caller; | |
7117c2d2 JA |
187 | { |
188 | char *tdir; | |
189 | ||
190 | FREE (the_current_working_directory); | |
191 | the_current_working_directory = (char *)NULL; | |
b80f6443 | 192 | tdir = get_working_directory (caller); |
7117c2d2 JA |
193 | return (tdir); |
194 | } | |
195 | ||
ac50fbac CR |
196 | static int |
197 | cdxattr (dir, ndirp) | |
198 | char *dir; /* don't assume we can always free DIR */ | |
199 | char **ndirp; /* return new constructed directory name */ | |
200 | { | |
201 | #if defined (O_XATTR) | |
202 | int apfd, fd, r, e; | |
203 | char buf[11+40+40]; /* construct new `fake' path for pwd */ | |
204 | ||
205 | apfd = openat (AT_FDCWD, dir, O_RDONLY|O_NONBLOCK); | |
206 | if (apfd < 0) | |
207 | return -1; | |
208 | fd = openat (apfd, ".", O_XATTR); | |
209 | e = errno; | |
210 | close (apfd); /* ignore close error for now */ | |
211 | errno = e; | |
212 | if (fd < 0) | |
213 | return -1; | |
214 | r = fchdir (fd); /* assume fchdir exists everywhere with O_XATTR */ | |
215 | if (r < 0) | |
216 | { | |
217 | close (fd); | |
218 | return -1; | |
219 | } | |
220 | /* NFSv4 and ZFS extended attribute directories do not have names which are | |
221 | visible in the standard Unix directory tree structure. To ensure we have | |
222 | a valid name for $PWD, we synthesize one under /proc, but to keep that | |
223 | path valid, we need to keep the file descriptor open as long as we are in | |
224 | this directory. This imposes a certain structure on /proc. */ | |
225 | if (ndirp) | |
226 | { | |
227 | sprintf (buf, "/proc/%d/fd/%d", getpid(), fd); | |
228 | *ndirp = savestring (buf); | |
229 | } | |
230 | ||
231 | if (xattrfd >= 0) | |
232 | close (xattrfd); | |
233 | xattrfd = fd; | |
234 | ||
235 | return r; | |
236 | #else | |
237 | return -1; | |
238 | #endif | |
239 | } | |
240 | ||
241 | /* Clean up the O_XATTR baggage. Currently only closes xattrfd */ | |
242 | static void | |
243 | resetxattr () | |
244 | { | |
245 | #if defined (O_XATTR) | |
246 | if (xattrfd >= 0) | |
247 | { | |
248 | close (xattrfd); | |
249 | xattrfd = -1; | |
250 | } | |
251 | #else | |
252 | xattrfd = -1; /* not strictly necessary */ | |
253 | #endif | |
254 | } | |
255 | ||
28ef6c31 JA |
256 | #define LCD_DOVARS 0x001 |
257 | #define LCD_DOSPELL 0x002 | |
258 | #define LCD_PRINTPATH 0x004 | |
495aee44 | 259 | #define LCD_FREEDIRNAME 0x008 |
28ef6c31 | 260 | |
726f6388 JA |
261 | /* This builtin is ultimately the way that all user-visible commands should |
262 | change the current working directory. It is called by cd_to_string (), | |
263 | so the programming interface is simple, and it handles errors and | |
264 | restrictions properly. */ | |
265 | int | |
266 | cd_builtin (list) | |
267 | WORD_LIST *list; | |
268 | { | |
ccc6cda3 | 269 | char *dirname, *cdpath, *path, *temp; |
a0c0a00f | 270 | int path_index, no_symlinks, opt, lflag, e; |
726f6388 JA |
271 | |
272 | #if defined (RESTRICTED_SHELL) | |
273 | if (restricted) | |
274 | { | |
7117c2d2 | 275 | sh_restricted ((char *)NULL); |
726f6388 JA |
276 | return (EXECUTION_FAILURE); |
277 | } | |
278 | #endif /* RESTRICTED_SHELL */ | |
279 | ||
495aee44 | 280 | eflag = 0; |
ccc6cda3 | 281 | no_symlinks = no_symbolic_links; |
ac50fbac | 282 | xattrflag = 0; |
ccc6cda3 | 283 | reset_internal_getopt (); |
ac50fbac CR |
284 | #if defined (O_XATTR) |
285 | while ((opt = internal_getopt (list, "eLP@")) != -1) | |
286 | #else | |
287 | while ((opt = internal_getopt (list, "eLP")) != -1) | |
288 | #endif | |
726f6388 | 289 | { |
ccc6cda3 | 290 | switch (opt) |
726f6388 | 291 | { |
ccc6cda3 JA |
292 | case 'P': |
293 | no_symlinks = 1; | |
294 | break; | |
295 | case 'L': | |
296 | no_symlinks = 0; | |
297 | break; | |
495aee44 CR |
298 | case 'e': |
299 | eflag = 1; | |
300 | break; | |
ac50fbac CR |
301 | #if defined (O_XATTR) |
302 | case '@': | |
303 | xattrflag = 1; | |
304 | break; | |
305 | #endif | |
a0c0a00f | 306 | CASE_HELPOPT; |
ccc6cda3 JA |
307 | default: |
308 | builtin_usage (); | |
ac50fbac | 309 | return (EX_USAGE); |
726f6388 | 310 | } |
726f6388 | 311 | } |
ccc6cda3 JA |
312 | list = loptend; |
313 | ||
28ef6c31 JA |
314 | lflag = (cdable_vars ? LCD_DOVARS : 0) | |
315 | ((interactive && cdspelling) ? LCD_DOSPELL : 0); | |
495aee44 CR |
316 | if (eflag && no_symlinks == 0) |
317 | eflag = 0; | |
28ef6c31 | 318 | |
ccc6cda3 | 319 | if (list == 0) |
726f6388 | 320 | { |
ccc6cda3 | 321 | /* `cd' without arguments is equivalent to `cd $HOME' */ |
726f6388 JA |
322 | dirname = get_string_value ("HOME"); |
323 | ||
ccc6cda3 | 324 | if (dirname == 0) |
726f6388 | 325 | { |
b80f6443 | 326 | builtin_error (_("HOME not set")); |
726f6388 JA |
327 | return (EXECUTION_FAILURE); |
328 | } | |
28ef6c31 | 329 | lflag = 0; |
726f6388 | 330 | } |
ac50fbac CR |
331 | #if defined (CD_COMPLAINS) |
332 | else if (list->next) | |
333 | { | |
334 | builtin_error (_("too many arguments")); | |
335 | return (EXECUTION_FAILURE); | |
336 | } | |
a0c0a00f CR |
337 | #endif |
338 | #if 0 | |
339 | else if (list->word->word[0] == '\0') | |
340 | { | |
341 | builtin_error (_("null directory")); | |
342 | return (EXECUTION_FAILURE); | |
343 | } | |
ac50fbac | 344 | #endif |
ccc6cda3 | 345 | else if (list->word->word[0] == '-' && list->word->word[1] == '\0') |
726f6388 | 346 | { |
ccc6cda3 JA |
347 | /* This is `cd -', equivalent to `cd $OLDPWD' */ |
348 | dirname = get_string_value ("OLDPWD"); | |
726f6388 | 349 | |
28ef6c31 | 350 | if (dirname == 0) |
726f6388 | 351 | { |
b80f6443 | 352 | builtin_error (_("OLDPWD not set")); |
726f6388 JA |
353 | return (EXECUTION_FAILURE); |
354 | } | |
b80f6443 | 355 | #if 0 |
28ef6c31 | 356 | lflag = interactive ? LCD_PRINTPATH : 0; |
b80f6443 JA |
357 | #else |
358 | lflag = LCD_PRINTPATH; /* According to SUSv3 */ | |
359 | #endif | |
726f6388 | 360 | } |
28ef6c31 JA |
361 | else if (absolute_pathname (list->word->word)) |
362 | dirname = list->word->word; | |
3185942a | 363 | else if (privileged_mode == 0 && (cdpath = get_string_value ("CDPATH"))) |
726f6388 | 364 | { |
ccc6cda3 JA |
365 | dirname = list->word->word; |
366 | ||
28ef6c31 JA |
367 | /* Find directory in $CDPATH. */ |
368 | path_index = 0; | |
369 | while (path = extract_colon_unit (cdpath, &path_index)) | |
726f6388 | 370 | { |
28ef6c31 JA |
371 | /* OPT is 1 if the path element is non-empty */ |
372 | opt = path[0] != '\0'; | |
373 | temp = sh_makepath (path, dirname, MP_DOTILDE); | |
374 | free (path); | |
cce855bc | 375 | |
ac50fbac | 376 | if (change_to_directory (temp, no_symlinks, xattrflag)) |
cce855bc | 377 | { |
28ef6c31 JA |
378 | /* POSIX.2 says that if a nonempty directory from CDPATH |
379 | is used to find the directory to change to, the new | |
380 | directory name is echoed to stdout, whether or not | |
381 | the shell is interactive. */ | |
b80f6443 JA |
382 | if (opt && (path = no_symlinks ? temp : the_current_working_directory)) |
383 | printf ("%s\n", path); | |
28ef6c31 JA |
384 | |
385 | free (temp); | |
95732b49 | 386 | #if 0 |
28ef6c31 JA |
387 | /* Posix.2 says that after using CDPATH, the resultant |
388 | value of $PWD will not contain `.' or `..'. */ | |
389 | return (bindpwd (posixly_correct || no_symlinks)); | |
95732b49 JA |
390 | #else |
391 | return (bindpwd (no_symlinks)); | |
392 | #endif | |
cce855bc | 393 | } |
28ef6c31 JA |
394 | else |
395 | free (temp); | |
726f6388 JA |
396 | } |
397 | ||
ac50fbac CR |
398 | #if 0 |
399 | /* changed for bash-4.2 Posix cd description steps 5-6 */ | |
28ef6c31 JA |
400 | /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't |
401 | try the current directory, so we just punt now with an error | |
402 | message if POSIXLY_CORRECT is non-zero. The check for cdpath[0] | |
403 | is so we don't mistakenly treat a CDPATH value of "" as not | |
404 | specifying the current directory. */ | |
405 | if (posixly_correct && cdpath[0]) | |
726f6388 | 406 | { |
28ef6c31 JA |
407 | builtin_error ("%s: %s", dirname, strerror (ENOENT)); |
408 | return (EXECUTION_FAILURE); | |
726f6388 | 409 | } |
495aee44 | 410 | #endif |
28ef6c31 JA |
411 | } |
412 | else | |
413 | dirname = list->word->word; | |
414 | ||
415 | /* When we get here, DIRNAME is the directory to change to. If we | |
416 | chdir successfully, just return. */ | |
ac50fbac | 417 | if (change_to_directory (dirname, no_symlinks, xattrflag)) |
28ef6c31 JA |
418 | { |
419 | if (lflag & LCD_PRINTPATH) | |
420 | printf ("%s\n", dirname); | |
421 | return (bindpwd (no_symlinks)); | |
422 | } | |
726f6388 | 423 | |
28ef6c31 JA |
424 | /* If the user requests it, then perhaps this is the name of |
425 | a shell variable, whose value contains the directory to | |
426 | change to. */ | |
427 | if (lflag & LCD_DOVARS) | |
428 | { | |
429 | temp = get_string_value (dirname); | |
ac50fbac | 430 | if (temp && change_to_directory (temp, no_symlinks, xattrflag)) |
726f6388 | 431 | { |
28ef6c31 JA |
432 | printf ("%s\n", temp); |
433 | return (bindpwd (no_symlinks)); | |
726f6388 | 434 | } |
28ef6c31 | 435 | } |
726f6388 | 436 | |
28ef6c31 JA |
437 | /* If the user requests it, try to find a directory name similar in |
438 | spelling to the one requested, in case the user made a simple | |
439 | typo. This is similar to the UNIX 8th and 9th Edition shells. */ | |
440 | if (lflag & LCD_DOSPELL) | |
441 | { | |
3185942a | 442 | temp = dirspell (dirname); |
ac50fbac | 443 | if (temp && change_to_directory (temp, no_symlinks, xattrflag)) |
28ef6c31 JA |
444 | { |
445 | printf ("%s\n", temp); | |
ac50fbac | 446 | free (temp); |
28ef6c31 JA |
447 | return (bindpwd (no_symlinks)); |
448 | } | |
449 | else | |
450 | FREE (temp); | |
726f6388 JA |
451 | } |
452 | ||
a0c0a00f CR |
453 | e = errno; |
454 | temp = printable_filename (dirname, 0); | |
455 | builtin_error ("%s: %s", temp, strerror (e)); | |
456 | if (temp != dirname) | |
457 | free (temp); | |
28ef6c31 | 458 | return (EXECUTION_FAILURE); |
726f6388 | 459 | } |
726f6388 | 460 | |
ccc6cda3 JA |
461 | $BUILTIN pwd |
462 | $FUNCTION pwd_builtin | |
95732b49 | 463 | $SHORT_DOC pwd [-LP] |
3185942a JA |
464 | Print the name of the current working directory. |
465 | ||
466 | Options: | |
467 | -L print the value of $PWD if it names the current working | |
a0c0a00f | 468 | directory |
3185942a JA |
469 | -P print the physical directory, without any symbolic links |
470 | ||
471 | By default, `pwd' behaves as if `-L' were specified. | |
472 | ||
473 | Exit Status: | |
474 | Returns 0 unless an invalid option is given or the current directory | |
475 | cannot be read. | |
726f6388 JA |
476 | $END |
477 | ||
ccc6cda3 JA |
478 | /* Non-zero means that pwd always prints the physical directory, without |
479 | symbolic links. */ | |
480 | static int verbatim_pwd; | |
481 | ||
482 | /* Print the name of the current working directory. */ | |
483 | int | |
484 | pwd_builtin (list) | |
726f6388 JA |
485 | WORD_LIST *list; |
486 | { | |
28ef6c31 | 487 | char *directory; |
95732b49 | 488 | int opt, pflag; |
726f6388 | 489 | |
ccc6cda3 | 490 | verbatim_pwd = no_symbolic_links; |
95732b49 | 491 | pflag = 0; |
ccc6cda3 JA |
492 | reset_internal_getopt (); |
493 | while ((opt = internal_getopt (list, "LP")) != -1) | |
726f6388 | 494 | { |
ccc6cda3 | 495 | switch (opt) |
726f6388 | 496 | { |
ccc6cda3 | 497 | case 'P': |
95732b49 | 498 | verbatim_pwd = pflag = 1; |
ccc6cda3 JA |
499 | break; |
500 | case 'L': | |
501 | verbatim_pwd = 0; | |
502 | break; | |
a0c0a00f | 503 | CASE_HELPOPT; |
ccc6cda3 JA |
504 | default: |
505 | builtin_usage (); | |
ac50fbac | 506 | return (EX_USAGE); |
726f6388 JA |
507 | } |
508 | } | |
ccc6cda3 | 509 | list = loptend; |
726f6388 | 510 | |
28ef6c31 | 511 | #define tcwd the_current_working_directory |
726f6388 | 512 | |
28ef6c31 JA |
513 | directory = tcwd ? (verbatim_pwd ? sh_physpath (tcwd, 0) : tcwd) |
514 | : get_working_directory ("pwd"); | |
b80f6443 JA |
515 | |
516 | /* Try again using getcwd() if canonicalization fails (for instance, if | |
517 | the file system has changed state underneath bash). */ | |
95732b49 JA |
518 | if ((tcwd && directory == 0) || |
519 | (posixly_correct && same_file (".", tcwd, (struct stat *)0, (struct stat *)0) == 0)) | |
ac50fbac CR |
520 | { |
521 | if (directory && directory != tcwd) | |
522 | free (directory); | |
523 | directory = resetpwd ("pwd"); | |
524 | } | |
b80f6443 | 525 | |
28ef6c31 | 526 | #undef tcwd |
726f6388 | 527 | |
ccc6cda3 JA |
528 | if (directory) |
529 | { | |
495aee44 | 530 | opt = EXECUTION_SUCCESS; |
ccc6cda3 | 531 | printf ("%s\n", directory); |
95732b49 JA |
532 | /* This is dumb but posix-mandated. */ |
533 | if (posixly_correct && pflag) | |
495aee44 | 534 | opt = setpwd (directory); |
28ef6c31 JA |
535 | if (directory != the_current_working_directory) |
536 | free (directory); | |
495aee44 | 537 | return (sh_chkwrite (opt)); |
726f6388 | 538 | } |
ccc6cda3 JA |
539 | else |
540 | return (EXECUTION_FAILURE); | |
726f6388 | 541 | } |
726f6388 JA |
542 | |
543 | /* Do the work of changing to the directory NEWDIR. Handle symbolic | |
28ef6c31 JA |
544 | link following, etc. This function *must* return with |
545 | the_current_working_directory either set to NULL (in which case | |
546 | getcwd() will eventually be called), or set to a string corresponding | |
547 | to the working directory. Return 1 on success, 0 on failure. */ | |
726f6388 JA |
548 | |
549 | static int | |
ac50fbac | 550 | change_to_directory (newdir, nolinks, xattr) |
726f6388 | 551 | char *newdir; |
ac50fbac | 552 | int nolinks, xattr; |
726f6388 | 553 | { |
ac50fbac | 554 | char *t, *tdir, *ndir; |
d233b485 | 555 | int err, canon_failed, r, ndlen; |
726f6388 | 556 | |
28ef6c31 | 557 | tdir = (char *)NULL; |
726f6388 | 558 | |
28ef6c31 JA |
559 | if (the_current_working_directory == 0) |
560 | { | |
561 | t = get_working_directory ("chdir"); | |
562 | FREE (t); | |
563 | } | |
726f6388 | 564 | |
28ef6c31 | 565 | t = make_absolute (newdir, the_current_working_directory); |
726f6388 | 566 | |
28ef6c31 JA |
567 | /* TDIR is either the canonicalized absolute pathname of NEWDIR |
568 | (nolinks == 0) or the absolute physical pathname of NEWDIR | |
569 | (nolinks != 0). */ | |
570 | tdir = nolinks ? sh_physpath (t, 0) | |
571 | : sh_canonpath (t, PATH_CHECKDOTDOT|PATH_CHECKEXISTS); | |
726f6388 | 572 | |
95732b49 | 573 | ndlen = strlen (newdir); |
95732b49 | 574 | |
28ef6c31 JA |
575 | /* Use the canonicalized version of NEWDIR, or, if canonicalization |
576 | failed, use the non-canonical form. */ | |
7117c2d2 | 577 | canon_failed = 0; |
28ef6c31 JA |
578 | if (tdir && *tdir) |
579 | free (t); | |
580 | else | |
581 | { | |
582 | FREE (tdir); | |
583 | tdir = t; | |
7117c2d2 JA |
584 | canon_failed = 1; |
585 | } | |
586 | ||
587 | /* In POSIX mode, if we're resolving symlinks logically and sh_canonpath | |
588 | returns NULL (because it checks the path, it will return NULL if the | |
589 | resolved path doesn't exist), fail immediately. */ | |
d233b485 | 590 | #if defined (ENAMETOOLONG) |
95732b49 | 591 | if (posixly_correct && nolinks == 0 && canon_failed && (errno != ENAMETOOLONG || ndlen > PATH_MAX)) |
d233b485 CR |
592 | #else |
593 | if (posixly_correct && nolinks == 0 && canon_failed && ndlen > PATH_MAX) | |
594 | #endif | |
7117c2d2 | 595 | { |
b80f6443 JA |
596 | #if defined ENAMETOOLONG |
597 | if (errno != ENOENT && errno != ENAMETOOLONG) | |
598 | #else | |
599 | if (errno != ENOENT) | |
600 | #endif | |
601 | errno = ENOTDIR; | |
95732b49 | 602 | free (tdir); |
7117c2d2 | 603 | return (0); |
28ef6c31 | 604 | } |
726f6388 | 605 | |
ac50fbac CR |
606 | #if defined (O_XATTR) |
607 | if (xattrflag) | |
608 | { | |
609 | r = cdxattr (nolinks ? newdir : tdir, &ndir); | |
610 | if (r >= 0) | |
611 | { | |
612 | canon_failed = 0; | |
613 | free (tdir); | |
614 | tdir = ndir; | |
615 | } | |
616 | else | |
617 | { | |
618 | err = errno; | |
619 | free (tdir); | |
620 | errno = err; | |
621 | return (0); /* no xattr */ | |
622 | } | |
623 | } | |
624 | else | |
625 | #endif | |
626 | { | |
627 | r = chdir (nolinks ? newdir : tdir); | |
628 | if (r >= 0) | |
629 | resetxattr (); | |
630 | } | |
631 | ||
28ef6c31 | 632 | /* If the chdir succeeds, update the_current_working_directory. */ |
ac50fbac | 633 | if (r == 0) |
28ef6c31 | 634 | { |
7117c2d2 JA |
635 | /* If canonicalization failed, but the chdir succeeded, reset the |
636 | shell's idea of the_current_working_directory. */ | |
637 | if (canon_failed) | |
7117c2d2 | 638 | { |
b80f6443 JA |
639 | t = resetpwd ("cd"); |
640 | if (t == 0) | |
641 | set_working_directory (tdir); | |
ac50fbac CR |
642 | else |
643 | free (t); | |
7117c2d2 | 644 | } |
b80f6443 JA |
645 | else |
646 | set_working_directory (tdir); | |
7117c2d2 | 647 | |
95732b49 | 648 | free (tdir); |
28ef6c31 JA |
649 | return (1); |
650 | } | |
726f6388 | 651 | |
28ef6c31 JA |
652 | /* We failed to change to the appropriate directory name. If we tried |
653 | what the user passed (nolinks != 0), punt now. */ | |
654 | if (nolinks) | |
95732b49 JA |
655 | { |
656 | free (tdir); | |
657 | return (0); | |
658 | } | |
726f6388 | 659 | |
28ef6c31 | 660 | err = errno; |
726f6388 | 661 | |
28ef6c31 JA |
662 | /* We're not in physical mode (nolinks == 0), but we failed to change to |
663 | the canonicalized directory name (TDIR). Try what the user passed | |
d233b485 CR |
664 | verbatim. If we succeed, reinitialize the_current_working_directory. |
665 | POSIX requires that we just fail here, so we do in posix mode. */ | |
666 | if (posixly_correct == 0 && chdir (newdir) == 0) | |
28ef6c31 | 667 | { |
b80f6443 JA |
668 | t = resetpwd ("cd"); |
669 | if (t == 0) | |
670 | set_working_directory (tdir); | |
671 | else | |
672 | free (t); | |
726f6388 | 673 | |
b80f6443 | 674 | r = 1; |
726f6388 JA |
675 | } |
676 | else | |
28ef6c31 JA |
677 | { |
678 | errno = err; | |
b80f6443 | 679 | r = 0; |
28ef6c31 | 680 | } |
b80f6443 JA |
681 | |
682 | free (tdir); | |
683 | return r; | |
ccc6cda3 | 684 | } |