X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=builtins%2Fcd.def;h=025e4f5e1f1209afe6700899aa7d89ec0ef128be;hb=95732b497d12c98613bb3c5db16b61f377501a59;hp=ef8d8051aa6765ea854dd3f5a2b1e1478e3e2b9a;hpb=cce855bc5b117cb7ae70064131120687bc69fac0;p=platform%2Fupstream%2Fbash.git diff --git a/builtins/cd.def b/builtins/cd.def index ef8d805..025e4f5 100644 --- a/builtins/cd.def +++ b/builtins/cd.def @@ -1,13 +1,13 @@ This file is cd.def, from which is created cd.c. It implements the builtins "cd" and "pwd" in Bash. -Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc. +Copyright (C) 1987-2005 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. Bash is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free -Software Foundation; either version 1, or (at your option) any later +Software Foundation; either version 2, or (at your option) any later version. Bash is distributed in the hope that it will be useful, but WITHOUT ANY @@ -17,7 +17,7 @@ for more details. You should have received a copy of the GNU General Public License along with Bash; see the file COPYING. If not, write to the Free Software -Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. $PRODUCES cd.c #include @@ -30,8 +30,8 @@ $PRODUCES cd.c #endif #include "../bashtypes.h" -#include "../posixdir.h" -#include "../posixstat.h" +#include "posixdir.h" +#include "posixstat.h" #ifndef _MINIX #include #endif @@ -39,13 +39,14 @@ $PRODUCES cd.c #include #include "../bashansi.h" +#include "../bashintl.h" #include #include #include "../shell.h" #include "../flags.h" -#include "../maxpath.h" +#include "maxpath.h" #include "common.h" #include "bashgetopt.h" @@ -53,14 +54,15 @@ $PRODUCES cd.c extern int errno; #endif /* !errno */ -extern int posixly_correct, interactive; +extern int posixly_correct; extern int array_needs_making; extern char *bash_getcwd_errstr; -static int change_to_directory (); +static int bindpwd __P((int)); +static void setpwd __P((char *)); +static int change_to_directory __P((char *, int)); -static char *cdspell (); -static int spname (), mindist (), spdist (); +static char *cdspell __P((char *)); /* Change this to 1 to get cd spelling correction by default. */ int cdspelling = 0; @@ -69,7 +71,7 @@ int cdable_vars; $BUILTIN cd $FUNCTION cd_builtin -$SHORT_DOC cd [-PL] [dir] +$SHORT_DOC cd [-L|-P] [dir] Change the current directory to DIR. The variable $HOME is the default DIR. The variable CDPATH defines the search path for the directory containing DIR. Alternative directory names in CDPATH @@ -83,43 +85,21 @@ instead of following symbolic links; the -L option forces symbolic links to be followed. $END -/* Take PATH, an element from $CDPATH, and DIR, a directory name, and paste - them together into PATH/DIR. Tilde expansion is performed on PATH if - DOTILDE is non-zero. If PATH is the empty string, it is converted to - `./', since a null element in $CDPATH means the current directory. */ -static char * -mkpath (path, dir, dotilde) - char *path, *dir; - int dotilde; +/* Just set $PWD, don't change OLDPWD. Used by `pwd -P' in posix mode. */ +static void +setpwd (dirname) + char *dirname; { - int dirlen, pathlen; - char *ret, *xpath; - - if (*path == '\0') - { - xpath = xmalloc (2); - xpath[0] = '.'; - xpath[1] = '\0'; - pathlen = 1; - } - else - { - xpath = (dotilde && *path == '~') ? bash_tilde_expand (path) : path; - pathlen = strlen (xpath); - } + int old_anm; + SHELL_VAR *tvar; - dirlen = strlen (dir); - ret = xmalloc (2 + dirlen + pathlen); - strcpy (ret, xpath); - if (xpath[pathlen - 1] != '/') + old_anm = array_needs_making; + tvar = bind_variable ("PWD", dirname ? dirname : "", 0); + if (old_anm == 0 && array_needs_making && exported_p (tvar)) { - ret[pathlen++] = '/'; - ret[pathlen] = '\0'; + update_export_env_inplace ("PWD=", 4, dirname ? dirname : ""); + array_needs_making = 0; } - strcpy (ret + pathlen, dir); - if (xpath != path) - free (xpath); - return (ret); } static int @@ -127,42 +107,51 @@ bindpwd (no_symlinks) int no_symlinks; { char *dirname, *pwdvar; - int old_symlinks, old_anm; + int old_anm; SHELL_VAR *tvar; - if (no_symlinks) - { - old_symlinks = no_symbolic_links; - no_symbolic_links = 1; - dirname = get_working_directory ("cd"); - no_symbolic_links = old_symlinks; - } - else - dirname = get_working_directory ("cd"); - - bind_variable ("OLDPWD", get_string_value ("PWD")); +#define tcwd the_current_working_directory + dirname = tcwd ? (no_symlinks ? sh_physpath (tcwd, 0) : tcwd) + : get_working_directory ("cd"); +#undef tcwd old_anm = array_needs_making; - tvar = bind_variable ("PWD", dirname); - /* This is an efficiency hack. If PWD is exported, we will need to - remake the exported environment every time we change directories. - If there is no other reason to make the exported environment, just - update PWD in place and mark the exported environment as no longer - needing a remake. */ + pwdvar = get_string_value ("PWD"); + + tvar = bind_variable ("OLDPWD", pwdvar, 0); if (old_anm == 0 && array_needs_making && exported_p (tvar)) { - pwdvar = xmalloc (STRLEN (dirname) + 5); /* 5 = "PWD" + '=' + '\0' */ - strcpy (pwdvar, "PWD="); - if (dirname) - strcpy (pwdvar + 4, dirname); - add_or_supercede_exported_var (pwdvar, 0); + update_export_env_inplace ("OLDPWD=", 7, pwdvar); array_needs_making = 0; } - FREE (dirname); + setpwd (dirname); + + if (dirname && dirname != the_current_working_directory) + free (dirname); + return (EXECUTION_SUCCESS); } +/* Call get_working_directory to reset the value of + the_current_working_directory () */ +static char * +resetpwd (caller) + char *caller; +{ + char *tdir; + + FREE (the_current_working_directory); + the_current_working_directory = (char *)NULL; + tdir = get_working_directory (caller); + return (tdir); +} + +#define LCD_DOVARS 0x001 +#define LCD_DOSPELL 0x002 +#define LCD_PRINTPATH 0x004 +#define LCD_FREEDIRNAME 0x010 + /* This builtin is ultimately the way that all user-visible commands should change the current working directory. It is called by cd_to_string (), so the programming interface is simple, and it handles errors and @@ -172,13 +161,12 @@ cd_builtin (list) WORD_LIST *list; { char *dirname, *cdpath, *path, *temp; - int path_index, no_symlinks, opt; - struct stat sb; + int path_index, no_symlinks, opt, lflag; #if defined (RESTRICTED_SHELL) if (restricted) { - builtin_error ("restricted"); + sh_restricted ((char *)NULL); return (EXECUTION_FAILURE); } #endif /* RESTRICTED_SHELL */ @@ -202,6 +190,9 @@ cd_builtin (list) } list = loptend; + lflag = (cdable_vars ? LCD_DOVARS : 0) | + ((interactive && cdspelling) ? LCD_DOSPELL : 0); + if (list == 0) { /* `cd' without arguments is equivalent to `cd $HOME' */ @@ -209,124 +200,122 @@ cd_builtin (list) if (dirname == 0) { - builtin_error ("HOME not set"); - return (EXECUTION_FAILURE); - } - - if (change_to_directory (dirname, no_symlinks) == 0) - { - builtin_error ("%s: %s", dirname, strerror (errno)); + builtin_error (_("HOME not set")); return (EXECUTION_FAILURE); } + lflag = 0; } else if (list->word->word[0] == '-' && list->word->word[1] == '\0') { /* This is `cd -', equivalent to `cd $OLDPWD' */ dirname = get_string_value ("OLDPWD"); - if (dirname == 0 || change_to_directory (dirname, no_symlinks) == 0) + if (dirname == 0) { - if (dirname == 0) - builtin_error ("OLDPWD not set"); - else - builtin_error ("%s: %s", dirname, strerror (errno)); + builtin_error (_("OLDPWD not set")); return (EXECUTION_FAILURE); } - if (interactive) - printf ("%s\n", dirname); +#if 0 + lflag = interactive ? LCD_PRINTPATH : 0; +#else + lflag = LCD_PRINTPATH; /* According to SUSv3 */ +#endif } - else + else if (absolute_pathname (list->word->word)) + dirname = list->word->word; + else if (cdpath = get_string_value ("CDPATH")) { dirname = list->word->word; - if (absolute_pathname (dirname) == 0 && (cdpath = get_string_value ("CDPATH"))) + /* Find directory in $CDPATH. */ + path_index = 0; + while (path = extract_colon_unit (cdpath, &path_index)) { - /* Find directory in $CDPATH. */ - path_index = 0; - while ((path = extract_colon_unit (cdpath, &path_index))) - { - /* OPT is 1 if the path element is non-empty */ - opt = path[0] != '\0'; - temp = mkpath (path, dirname, 1); - free (path); - - if (stat (temp, &sb) < 0 || S_ISDIR (sb.st_mode) == 0) - { - free (temp); - continue; - } - - if (change_to_directory (temp, no_symlinks)) - { - /* POSIX.2 says that if a nonempty directory from CDPATH - is used to find the directory to change to, the new - directory name is echoed to stdout, whether or not - the shell is interactive. */ - if (opt) - printf ("%s\n", the_current_working_directory); - - free (temp); - /* Posix.2 says that after using CDPATH, the resultant - value of $PWD will not contain symlinks. */ - return (bindpwd (posixly_correct || no_symlinks)); - } - else - free (temp); - } + /* OPT is 1 if the path element is non-empty */ + opt = path[0] != '\0'; + temp = sh_makepath (path, dirname, MP_DOTILDE); + free (path); - /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't - try the current directory, so we just punt now with an error - message if POSIXLY_CORRECT is non-zero. */ - if (posixly_correct) + if (change_to_directory (temp, no_symlinks)) { - builtin_error ("%s: %s", dirname, strerror (ENOENT)); - return (EXECUTION_FAILURE); + /* POSIX.2 says that if a nonempty directory from CDPATH + is used to find the directory to change to, the new + directory name is echoed to stdout, whether or not + the shell is interactive. */ + if (opt && (path = no_symlinks ? temp : the_current_working_directory)) + printf ("%s\n", path); + + free (temp); +#if 0 + /* Posix.2 says that after using CDPATH, the resultant + value of $PWD will not contain `.' or `..'. */ + return (bindpwd (posixly_correct || no_symlinks)); +#else + return (bindpwd (no_symlinks)); +#endif } + else + free (temp); } - if (change_to_directory (dirname, no_symlinks)) - return (bindpwd (no_symlinks)); - - /* If the user requests it, then perhaps this is the name of - a shell variable, whose value contains the directory to - change to. If that is the case, then change to that - directory. */ - if (cdable_vars) + /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't + try the current directory, so we just punt now with an error + message if POSIXLY_CORRECT is non-zero. The check for cdpath[0] + is so we don't mistakenly treat a CDPATH value of "" as not + specifying the current directory. */ + if (posixly_correct && cdpath[0]) { - temp = get_string_value (dirname); - if (temp && change_to_directory (temp, no_symlinks)) - { - printf ("%s\n", temp); - return (bindpwd (no_symlinks)); - } + builtin_error ("%s: %s", dirname, strerror (ENOENT)); + return (EXECUTION_FAILURE); } + } + else + dirname = list->word->word; + + /* When we get here, DIRNAME is the directory to change to. If we + chdir successfully, just return. */ + if (change_to_directory (dirname, no_symlinks)) + { + if (lflag & LCD_PRINTPATH) + printf ("%s\n", dirname); + return (bindpwd (no_symlinks)); + } - /* If the user requests it, try to find a directory name similar in - spelling to the one requested, in case the user made a simple - typo. This is similar to the UNIX 8th and 9th Edition shells. */ - if (interactive && cdspelling) + /* If the user requests it, then perhaps this is the name of + a shell variable, whose value contains the directory to + change to. */ + if (lflag & LCD_DOVARS) + { + temp = get_string_value (dirname); + if (temp && change_to_directory (temp, no_symlinks)) { - temp = cdspell (dirname); - if (temp && change_to_directory (temp, no_symlinks)) - { - printf ("%s\n", temp); - free (temp); - return (bindpwd (no_symlinks)); - } - else - FREE (temp); + printf ("%s\n", temp); + return (bindpwd (no_symlinks)); } + } - builtin_error ("%s: %s", dirname, strerror (errno)); - return (EXECUTION_FAILURE); + /* If the user requests it, try to find a directory name similar in + spelling to the one requested, in case the user made a simple + typo. This is similar to the UNIX 8th and 9th Edition shells. */ + if (lflag & LCD_DOSPELL) + { + temp = cdspell (dirname); + if (temp && change_to_directory (temp, no_symlinks)) + { + printf ("%s\n", temp); + return (bindpwd (no_symlinks)); + } + else + FREE (temp); } - return (bindpwd (no_symlinks)); + builtin_error ("%s: %s", dirname, strerror (errno)); + return (EXECUTION_FAILURE); } $BUILTIN pwd $FUNCTION pwd_builtin -$SHORT_DOC pwd [-PL] +$SHORT_DOC pwd [-LP] Print the current working directory. With the -P option, pwd prints the physical directory, without any symbolic links; the -L option makes pwd follow symbolic links. @@ -341,17 +330,18 @@ int pwd_builtin (list) WORD_LIST *list; { - char *directory, *buffer; - int opt; + char *directory; + int opt, pflag; verbatim_pwd = no_symbolic_links; + pflag = 0; reset_internal_getopt (); while ((opt = internal_getopt (list, "LP")) != -1) { switch (opt) { case 'P': - verbatim_pwd = 1; + verbatim_pwd = pflag = 1; break; case 'L': verbatim_pwd = 0; @@ -363,25 +353,35 @@ pwd_builtin (list) } list = loptend; - if (verbatim_pwd) - { - buffer = xmalloc (PATH_MAX); - directory = getcwd (buffer, PATH_MAX); +#define tcwd the_current_working_directory - if (directory == 0) - { - builtin_error ("%s: %s", bash_getcwd_errstr, strerror (errno)); - free (buffer); - } - } - else - directory = get_working_directory ("pwd"); + directory = tcwd ? (verbatim_pwd ? sh_physpath (tcwd, 0) : tcwd) + : get_working_directory ("pwd"); + + /* Try again using getcwd() if canonicalization fails (for instance, if + the file system has changed state underneath bash). */ + if ((tcwd && directory == 0) || + (posixly_correct && same_file (".", tcwd, (struct stat *)0, (struct stat *)0) == 0)) + directory = resetpwd ("pwd"); + +#undef tcwd if (directory) { printf ("%s\n", directory); + /* This is dumb but posix-mandated. */ + if (posixly_correct && pflag) + setpwd (directory); + if (directory != the_current_working_directory) + free (directory); fflush (stdout); - free (directory); + if (ferror (stdout)) + { + sh_wrerror (); + clearerr (stdout); + return (EXECUTION_FAILURE); + } + return (EXECUTION_SUCCESS); } else @@ -389,83 +389,114 @@ pwd_builtin (list) } /* Do the work of changing to the directory NEWDIR. Handle symbolic - link following, etc. */ + link following, etc. This function *must* return with + the_current_working_directory either set to NULL (in which case + getcwd() will eventually be called), or set to a string corresponding + to the working directory. Return 1 on success, 0 on failure. */ static int change_to_directory (newdir, nolinks) char *newdir; int nolinks; { - char *t; + char *t, *tdir; + int err, canon_failed, r, ndlen, dlen; - if (nolinks == 0) + tdir = (char *)NULL; + + if (the_current_working_directory == 0) { - int chdir_return = 0; - char *tdir = (char *)NULL; + t = get_working_directory ("chdir"); + FREE (t); + } - if (the_current_working_directory == 0) - { - t = get_working_directory ("cd_links"); - FREE (t); - } + t = make_absolute (newdir, the_current_working_directory); - if (the_current_working_directory) - t = make_absolute (newdir, the_current_working_directory); - else - t = savestring (newdir); + /* TDIR is either the canonicalized absolute pathname of NEWDIR + (nolinks == 0) or the absolute physical pathname of NEWDIR + (nolinks != 0). */ + tdir = nolinks ? sh_physpath (t, 0) + : sh_canonpath (t, PATH_CHECKDOTDOT|PATH_CHECKEXISTS); - /* TDIR is the canonicalized absolute pathname of the NEWDIR. */ - tdir = canonicalize_pathname (t); + ndlen = strlen (newdir); + dlen = strlen (t); - /* Use the canonicalized version of NEWDIR, or, if canonicalization - failed, use the non-canonical form. */ - if (tdir && *tdir) - free (t); - else + /* Use the canonicalized version of NEWDIR, or, if canonicalization + failed, use the non-canonical form. */ + canon_failed = 0; + if (tdir && *tdir) + free (t); + else + { + FREE (tdir); + tdir = t; + canon_failed = 1; + } + + /* In POSIX mode, if we're resolving symlinks logically and sh_canonpath + returns NULL (because it checks the path, it will return NULL if the + resolved path doesn't exist), fail immediately. */ + if (posixly_correct && nolinks == 0 && canon_failed && (errno != ENAMETOOLONG || ndlen > PATH_MAX)) + { +#if defined ENAMETOOLONG + if (errno != ENOENT && errno != ENAMETOOLONG) +#else + if (errno != ENOENT) +#endif + errno = ENOTDIR; + free (tdir); + return (0); + } + + /* If the chdir succeeds, update the_current_working_directory. */ + if (chdir (nolinks ? newdir : tdir) == 0) + { + /* If canonicalization failed, but the chdir succeeded, reset the + shell's idea of the_current_working_directory. */ + if (canon_failed) { - FREE (tdir); - tdir = t; + t = resetpwd ("cd"); + if (t == 0) + set_working_directory (tdir); } + else + set_working_directory (tdir); - if (chdir (tdir) < 0) - { - int err; + free (tdir); + return (1); + } - chdir_return = 0; - free (tdir); + /* We failed to change to the appropriate directory name. If we tried + what the user passed (nolinks != 0), punt now. */ + if (nolinks) + { + free (tdir); + return (0); + } - err = errno; + err = errno; - /* We failed changing to the canonicalized directory name. Try - what the user passed verbatim. If we succeed, reinitialize - the_current_working_directory. */ - if (chdir (newdir) == 0) - { - chdir_return = 1; - if (the_current_working_directory) - { - free (the_current_working_directory); - the_current_working_directory = (char *)NULL; - } - - tdir = get_working_directory ("cd"); - FREE (tdir); - } - else - errno = err; - } + /* We're not in physical mode (nolinks == 0), but we failed to change to + the canonicalized directory name (TDIR). Try what the user passed + verbatim. If we succeed, reinitialize the_current_working_directory. */ + if (chdir (newdir) == 0) + { + t = resetpwd ("cd"); + if (t == 0) + set_working_directory (tdir); else - { - chdir_return = 1; - - FREE (the_current_working_directory); - the_current_working_directory = tdir; - } + free (t); - return (chdir_return); + r = 1; } else - return (chdir (newdir) == 0); + { + errno = err; + r = 0; + } + + free (tdir); + return r; } /* Code for cd spelling correction. Original patch submitted by @@ -479,7 +510,7 @@ cdspell (dirname) char *guess; n = (strlen (dirname) * 3 + 1) / 2 + 1; - guess = xmalloc (n); + guess = (char *)xmalloc (n); switch (spname (dirname, guess)) { @@ -492,147 +523,3 @@ cdspell (dirname) return guess; } } - -/* - * `spname' and its helpers are inspired by the code in "The UNIX - * Programming Environment, Kernighan & Pike, Prentice-Hall 1984", - * pages 209 - 213. - */ - -/* - * `spname' -- return a correctly spelled filename - * - * int spname(char * oldname, char * newname) - * Returns: -1 if no reasonable match found - * 0 if exact match found - * 1 if corrected - * Stores corrected name in `newname'. - */ -static int -spname(oldname, newname) - char *oldname; - char *newname; -{ - char *op, *np, *p; - char guess[PATH_MAX + 1], best[PATH_MAX + 1]; - - op = oldname; - np = newname; - for (;;) - { - while (*op == '/') /* Skip slashes */ - *np++ = *op++; - *np = '\0'; - - if (*op == '\0') /* Exact or corrected */ - { - /* `.' is rarely the right thing. */ - if (oldname[1] == '\0' && newname[1] == '\0' && - oldname[0] != '.' && newname[0] == '.') - return -1; - return strcmp(oldname, newname) != 0; - } - - /* Copy next component into guess */ - for (p = guess; *op != '/' && *op != '\0'; op++) - if (p < guess + PATH_MAX) - *p++ = *op; - *p = '\0'; - - if (mindist(newname, guess, best) >= 3) - return -1; /* Hopeless */ - - /* - * Add to end of newname - */ - for (p = best; *np = *p++; np++) - ; - } -} - -/* - * Search directory for a guess - */ -static int -mindist(dir, guess, best) - char *dir; - char *guess; - char *best; -{ - DIR *fd; - struct dirent *dp; - int dist, x; - - dist = 3; /* Worst distance */ - if (*dir == '\0') - dir = "."; - - if ((fd = opendir(dir)) == NULL) - return dist; - - while ((dp = readdir(fd)) != NULL) - { - /* - * Look for a better guess. If the new guess is as - * good as the current one, we take it. This way, - * any single character match will be a better match - * than ".". - */ - x = spdist(dp->d_name, guess); - if (x <= dist && x != 3) - { - strcpy(best, dp->d_name); - dist = x; - if (dist == 0) /* Exact match */ - break; - } - } - (void)closedir(fd); - - /* Don't return `.' */ - if (best[0] == '.' && best[1] == '\0') - dist = 3; - return dist; -} - -/* - * `spdist' -- return the "distance" between two names. - * - * int spname(char * oldname, char * newname) - * Returns: 0 if strings are identical - * 1 if two characters are transposed - * 2 if one character is wrong, added or deleted - * 3 otherwise - */ -static int -spdist(cur, new) - char *cur, *new; -{ - while (*cur == *new) - { - if (*cur == '\0') - return 0; /* Exact match */ - cur++; - new++; - } - - if (*cur) - { - if (*new) - { - if (cur[1] && new[1] && cur[0] == new[1] && cur[1] == new[0] && strcmp (cur + 2, new + 2) == 0) - return 1; /* Transposition */ - - if (strcmp (cur + 1, new + 1) == 0) - return 2; /* One character mismatch */ - } - - if (strcmp(&cur[1], &new[0]) == 0) - return 2; /* Extra character */ - } - - if (*new && strcmp(cur, new + 1) == 0) - return 2; /* Missing character */ - - return 3; -}