about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--Doc/Zsh/expn.yo3
-rw-r--r--Src/compat.c32
-rw-r--r--Src/hist.c32
-rw-r--r--configure.ac3
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  <pws@csr.com>
+
+	* 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  <p.w.stephenson@ntlworld.com>
 
 	* 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