From cb6856d115b26fbe6d78e3e730a57e9c0e05e8b9 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 19 Mar 2009 15:00:18 +0000 Subject: 26754: tweak zgetdir() and test for realpath() --- ChangeLog | 7 ++++++- Doc/Zsh/expn.yo | 3 ++- Src/compat.c | 32 ++++++++++++++++++++++++++++++-- Src/hist.c | 32 +++++++++++++++++++++++++++++--- configure.ac | 3 ++- 5 files changed, 69 insertions(+), 8 deletions(-) diff --git a/ChangeLog b/ChangeLog index 41fddecc1..8b91e7acf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2009-03-19 Peter Stephenson + + * 26754: configure.ac, Doc/Zsh/expn.yo, Src/compat.c, Src/hist.c: + tweak zgetdir() (but don't use it) and test for realpath(). + 2009-03-18 Peter Stephenson * 26752: Src/hist.c: fix ../ removal in :a and metafication @@ -11443,5 +11448,5 @@ ***************************************************** * This is used by the shell to define $ZSH_PATCHLEVEL -* $Revision: 1.4624 $ +* $Revision: 1.4625 $ ***************************************************** diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index 16d42fbfc..74dda0977 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -223,7 +223,8 @@ if necessary, and resolves any use of `tt(..)' and `tt(.)' in the path. item(tt(A))( As `tt(a)', but also resolve use of symbolic links where possible. Note that resolution of `tt(..)' occurs em(before) resolution of symbolic -links. +links. This call is equivalent to tt(a) unless your system has the +tt(realpath) system call (modern systems do). ) item(tt(e))( Remove all but the extension. diff --git a/Src/compat.c b/Src/compat.c index 38f24d7fe..ec3a8bc39 100644 --- a/Src/compat.c +++ b/Src/compat.c @@ -227,6 +227,26 @@ zopenmax(void) } #endif +/* + * Rationalise the current directory, returning the string. + * + * If "d" is not NULL, it is used to store information about the + * directory. The returned name is also present in d->dirname and is in + * permanently allocated memory. The handling of this case depends on + * whether the fchdir() system call is available; if it is, it is assumed + * the caller is able to restore the current directory. On successfully + * identifying the directory the function returns immediately rather + * than ascending the hierarchy. + * + * If "d" is NULL, no assumption about the caller's behaviour is + * made. The returned string is in heap memory. This case is + * always handled by changing directory up the hierarchy. + * + * On Cygwin or other systems where USE_GETCWD is defined (at the + * time of writing only QNX), we skip all the above and use the + * getcwd() system call. + */ + /**/ mod_export char * zgetdir(struct dirsav *d) @@ -257,25 +277,30 @@ zgetdir(struct dirsav *d) return buf; } + /* Record the initial inode and device */ pino = sbuf.st_ino; pdev = sbuf.st_dev; if (d) d->ino = pino, d->dev = pdev; +#if !defined(__CYGWIN__) && !defined(USE_GETCWD) #ifdef HAVE_FCHDIR else #endif -#if !defined(__CYGWIN__) && !defined(USE_GETCWD) holdintr(); for (;;) { + /* Examine the parent of the current directory. */ if (stat("..", &sbuf) < 0) break; + /* Inode and device of curtent directory */ ino = pino; dev = pdev; + /* Inode and device of current directory's parent */ pino = sbuf.st_ino; pdev = sbuf.st_dev; + /* If they're the same, we've reached the root directory. */ if (ino == pino && dev == pdev) { if (!buf[pos]) buf[--pos] = '/'; @@ -291,6 +316,7 @@ zgetdir(struct dirsav *d) return buf + pos; } + /* Search the parent for the current directory. */ if (!(dir = opendir(".."))) break; @@ -303,6 +329,7 @@ zgetdir(struct dirsav *d) continue; #ifdef HAVE_STRUCT_DIRENT_D_STAT if(de->d_stat.st_dev == dev && de->d_stat.st_ino == ino) { + /* Found the directory we're currently in */ strncpy(nbuf + 3, fn, PATH_MAX); break; } @@ -311,6 +338,7 @@ zgetdir(struct dirsav *d) if (dev != pdev || (ino_t) de->d_ino == ino) # endif /* HAVE_STRUCT_DIRENT_D_INO */ { + /* Maybe found directory, need to check device & inode */ strncpy(nbuf + 3, fn, PATH_MAX); lstat(nbuf, &sbuf); if (sbuf.st_dev == dev && sbuf.st_ino == ino) @@ -320,7 +348,7 @@ zgetdir(struct dirsav *d) } closedir(dir); if (!de) - break; + break; /* Not found */ len = strlen(nbuf + 2); pos -= len; while (pos <= 1) { diff --git a/Src/hist.c b/Src/hist.c index 4a2c12530..b843a2399 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -1522,7 +1522,7 @@ chabspath(char **junkptr) current += 3; } #endif - + for (;;) { if (*current == '/') { #ifdef __CYGWIN__ @@ -1579,7 +1579,14 @@ chabspath(char **junkptr) int chrealpath(char **junkptr) { + char *str; +#ifdef HAVE_CANONICALIZE_FILE_NAME + char *lastpos, *nonreal, *real; +#else +# ifdef HAVE_REAL_PATH char *lastpos, *nonreal, real[PATH_MAX]; +# endif +#endif if (!**junkptr) return 1; @@ -1588,6 +1595,9 @@ chrealpath(char **junkptr) if (!chabspath(junkptr)) return 0; +#if !defined(HAVE_REALPATH) && !defined(HAVE_CANONICALIZE_FILE_NAME) + return 1; +#else /* * Notice that this means you cannot pass relative paths into this * function! @@ -1600,7 +1610,19 @@ chrealpath(char **junkptr) lastpos = strend(*junkptr); nonreal = lastpos + 1; - while (!realpath(*junkptr, real)) { + while (! +#ifdef HAVE_CANONICALIZE_FILE_NAME + /* + * This is a GNU extension to realpath(); it's the + * same as calling realpath() with a NULL second argument + * which uses malloc() to get memory. The alternative + * interface is easier to test for, however. + */ + (real = canonicalize_file_name(*junkptr)) +#else + realpath(*junkptr, real) +#endif + ) { if (errno == EINVAL || errno == ELOOP || errno == ENAMETOOLONG || errno == ENOMEM) return 0; @@ -1615,7 +1637,7 @@ chrealpath(char **junkptr) *nonreal = '\0'; } - char *str = nonreal; + str = nonreal; while (str <= lastpos) { if (*str == '\0') *str = '/'; @@ -1623,6 +1645,10 @@ chrealpath(char **junkptr) } *junkptr = metafy(bicat(real, nonreal), -1, META_HEAPDUP); +#ifdef HAVE_CANONICALIZE_FILE_NAME + free(real); +#endif +#endif return 1; } diff --git a/configure.ac b/configure.ac index fb145ef3e..07002d3c6 100644 --- a/configure.ac +++ b/configure.ac @@ -1157,7 +1157,8 @@ AC_CHECK_FUNCS(strftime strptime mktime timelocal \ grantpt unlockpt ptsname \ htons ntohs \ regcomp regexec regerror regfree \ - gdbm_open getxattr) + gdbm_open getxattr \ + realpath canonicalize_file_name) AC_FUNC_STRCOLL if test x$enable_cap = xyes; then -- cgit 1.4.1