Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: _canonical_path not working on *BSD



On Wed, 26 Mar 2008 16:46:15 +0000
Peter Stephenson <pws@xxxxxxx> wrote:
> +_canonical_paths_get_canonical_path() {
> +  typeset newfile
> +  typeset -A seen
> +
> +  REPLY=$1
> +  # Guard against loops.
> +  while [[ -z ${seen[$REPLY]} ]]; do
> +    seen[$REPLY]=1
> +    newfile=$(zstat +link $REPLY 2>/dev/null)
> +    if [[ -n $newfile ]]; then
> +      REPLY=$newfile
> +    else
> +      break
> +    fi
> +  done
> +}

I should make one more point about this before leaving the subject: this
doesn't do everything that "readlink -f" does, in particular it doesn't
remove .. path segments, strip multiple /'s or remove symbolic link
references in intervening directories.

Luckily, we have the technology, I think.

We don't need the $(...) for the zstat.  It would be quite nice to
be able to get the directory canonicalization without a fork, too.

Index: Completion/Unix/Type/_canonical_paths
===================================================================
RCS file: /cvsroot/zsh/zsh/Completion/Unix/Type/_canonical_paths,v
retrieving revision 1.2
diff -u -r1.2 _canonical_paths
--- Completion/Unix/Type/_canonical_paths	26 Mar 2008 17:22:25 -0000	1.2
+++ Completion/Unix/Type/_canonical_paths	27 Mar 2008 10:19:10 -0000
@@ -36,20 +36,41 @@
 typeset -a matches files
 
 _canonical_paths_get_canonical_path() {
-  typeset newfile
+  typeset newfile dir
   typeset -A seen
 
   REPLY=$1
-  # Guard against loops.
+  # Resolve any trailing symbolic links, guarding against loops.
   while [[ -z ${seen[$REPLY]} ]]; do
     seen[$REPLY]=1
-    newfile=$(zstat +link $REPLY 2>/dev/null)
-    if [[ -n $newfile ]]; then
-      REPLY=$newfile
+    newfile=()
+    zstat -A newfile +link $REPLY 2>/dev/null
+    if [[ -n $newfile[1] ]]; then
+      REPLY=$newfile[1]
     else
       break
     fi
   done
+
+  # Canonicalise the directory path.  We may not be able to
+  # do this if we can't read all components.
+  if [[ -d $REPLY ]]; then
+    dir="$(unfunction chpwd
+           setopt CHASE_LINKS
+           cd $REPLY 2>/dev/null && pwd)"
+    if [[ -n $dir ]]; then
+      REPLY=$dir
+    fi
+  elif [[ $REPLY = */*[^/] && $REPLY != /[^/]# ]]; then
+    # Don't try this if there's a trailing slash or we're in
+    # the root directory.
+    dir="$(unfunction chpwd
+           setopt CHASE_LINKS
+           cd ${REPLY%/*} 2>/dev/null && pwd)"
+    if [[ -n $dir ]]; then
+      REPLY=$dir/${REPLY##*/}
+    fi
+  fi
 }
 
 



Messages sorted by: Reverse Date, Date, Thread, Author