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