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