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

PATCH: Modifiers that implement realpath-like feature



Hey zshers,

I'm particularly proud of this patch, as it's my first time contributing to an open source project. (I'm sure the novelty will wear off in a while.) It implements two history-style realpath-like modifiers for getting the absolute path of a file. I chose the letter 'a' for 'absolute' (r was already taken). Both 'a' and 'A' have different meanings. The uppercase A will resolve symbolic links.

There are a couple of caveats. Firstly, blank variables don't expand to anything. Secondly, the modifier does NOT care if the file exists. So:

% FILE='dne.txt'  # dne.txt does not exist.
%print ${FILE:a}
/home/joe/dne.txt

That example used the lower case 'a', which only resolves './' and '../'. However, with the upper case 'A' modifier, any of the previous will do :a and symlink resolution. Again, it doesn't matter if the file doesn't exist.

% ln -s /media/cdrom0 a_link
% FILE='./a_link/dne.txt'
% print ${FILE:A}
/media/cdrom0/dne.txt

Third, trying to climb above root is allowed, but ignored. Consider:

% FILE='/../../../dne.txt'
% print ${FILE:a}
/dne.txt

The only time there will ever be '..'s in the expansion is if RFS_SUPERROOT is enabled. I've never heard of it, but since fixdir() in builtin.c (upon which my functions are based) has it, I added it in.

That's the new modifiers in a nutshell. The diff is pasted below. I'm curious about the difference between bicat() and dyncat(), tricat() and zhtricat(). I wasn't sure if one was more appropriate to use than the other, or if it didn't matter. In any case, enjoy!

Michael "Nomexous" Hwang
-----------------

diff --git a/Src/hist.c b/Src/hist.c                                                                                                                                                                      
index 38ceac3..0dd8c21 100644                                                                                                                                                                             
--- a/Src/hist.c                                                                                                                                                                                          
+++ b/Src/hist.c                                                                                                                                                                                          
@@ -623,6 +623,21 @@ histsubchar(int c)                                                                                                                                                                   
            case 'p':                                                                                                                                                                                     
                histdone = HISTFLAG_DONE | HISTFLAG_NOEXEC;                                                                                                                                               
                break;                                                                                                                                                                                    
+           case 'a':                                                                                                                                                                                     
+               if (!chabspath(&sline)) {                                                                                                                                                                 
+                   herrflush();                                                                                                                                                                          
+                   zerr("modifier failed: a");                                                                                                                                                           
+                   return -1;                                                                                                                                                                            
+               }                                                                                                                                                                                         
+               break;                                                                                                                                                                                    
+                                                                                                                                                                                                         
+           case 'A':                                                                                                                                                                                     
+               if (!chrealpath(&sline)) {                                                                                                                                                                
+                   herrflush();                                                                                                                                                                          
+                   zerr("modifier failed: A");                                                                                                                                                           
+                   return -1;                                                                                                                                                                            
+               }                                                                                                                                                                                         
+               break;                                                                                                                                                                                    
            case 'h':                                                                                                                                                                                     
                if (!remtpath(&sline)) {                                                                                                                                                                  
                    herrflush();                                                                                                                                                                          
@@ -1484,6 +1499,119 @@ hcomsearch(char *str)                                                                                                                                                             
                                                                                                                                                                                                          
 /**/                                                                                                                                                                                                     
 int                                                                                                                                                                                                      
+chabspath(char **junkptr)                                                                                                                                                                                
+{                                                                                                                                                                                                        
+    if (!**junkptr)                                                                                                                                                                                      
+       return 1;                                                                                                                                                                                         
+                                                                                                                                                                                                         
+    if (**junkptr != '/') {                                                                                                                                                                              
+       *junkptr = zhtricat(zgetcwd(), "/", *junkptr);                                                                                                                                                    
+    }                                                                                                                                                                                                    
+                                                                                                                                                                                                         
+    char *current = *junkptr;                                                                                                                                                                            
+    char *dest = *junkptr;                                                                                                                                                                               
+                                                                                                                                                                                                         
+#ifdef HAVE_SUPERROOT                                                                                                                                                                                    
+    while (*current == '/' && current[1] == '.' && current[2] == '.' && (!current[3] || current[3] == '/')) {                                                                                            
+       *dest++ = '/';                                                                                                                                                                                    
+       *dest++ = '.';                                                                                                                                                                                    
+       *dest++ = '.';                                                                                                                                                                                    
+       current += 3;                                                                                                                                                                                     
+    }                                                                                                                                                                                                    
+#endif                                                                                                                                                                                                   
+                                                                                                                                                                                                         
+    for (;;) {                                                                                                                                                                                           
+       if (*current == '/') {                                                                                                                                                                            
+#ifdef __CYGWIN__                                                                                                                                                                                        
+           if (current == *junkptr && current[1] == '/')                                                                                                                                                 
+               *dest++ = *current++;                                                                                                                                                                     
+#endif                                                                                                                                                                                                   
+           *dest++ = *current++;                                                                                                                                                                         
+           while (*current == '/')                                                                                                                                                                       
+               current++;                                                                                                                                                                                
+       } else if (!*current) {                                                                                                                                                                           
+           while (dest > *junkptr + 1 && dest[-1] == '/')                                                                                                                                                
+               dest--;                                                                                                                                                                                   
+           *dest = '\0';                                                                                                                                                                                 
+           break;                                                                                                                                                                                        
+       } else if (current[0] == '.' && current[1] == '.' && (!current[2] || current[2] == '/')) {                                                                                                        
+               if (current == *junkptr || dest == *junkptr) {                                                                                                                                            
+                   *dest++ = '.';                                                                                                                                                                        
+                   *dest++ = '.';                                                                                                                                                                        
+                   current += 2;                                                                                                                                                                         
+               } else if (dest > *junkptr + 2 && !strncmp(dest - 3, "../", 3)) {                                                                                                                         
+                   *dest++ = '.';                                                                                                                                                                        
+                   *dest++ = '.';                                                                                                                                                                        
+                   current += 2;                                                                                                                                                                         
+               } else if (dest > *junkptr + 1) {                                                                                                                                                         
+                   *dest = '\0';                                                                                                                                                                         
+                   for (dest--; dest > *junkptr + 1 && dest[-1] != '/'; dest--);                                                                                                                         
+                   if (dest[-1] != '/')                                                                                                                                                                  
+                       dest--;                                                                                                                                                                           
+                   current += 2;                                                                                                                                                                         
+               } else if (dest == *junkptr + 1) { /* This might break with Cygwin's leading double slashes? */                                                                                           
+                   current += 2;                                                                                                                                                                         
+               } else {                                                                                                                                                                                  
+                   return 0;                                                                                                                                                                             
+               }                                                                                                                                                                                         
+       } else if (current[0] == '.' && (current[1] == '/' || !current[1])) {                                                                                                                             
+            while (*++current == '/');                                                                                                                                                                   
+       } else {                                                                                                                                                                                          
+           while (*current != '/' && *current != '\0')                                                                                                                                                   
+               if ((*dest++ = *current++) == Meta)                                                                                                                                                       
+                   dest[-1] = *current++ ^ 32;                                                                                                                                                           
+       }                                                                                                                                                                                                 
+    }                                                                                                                                                                                                    
+    return 1;                                                                                                                                                                                            
+}                                                                                                                                                                                                        
+                                                                                                                                                                                                         
+/**/                                                                                                                                                                                                     
+int                                                                                                                                                                                                      
+chrealpath(char **junkptr)                                                                                                                                                                               
+{                                                                                                                                                                                                        
+    if (!**junkptr)                                                                                                                                                                                      
+       return 1;                                                                                                                                                                                         
+                                                                                                                                                                                                         
+    /* Notice that this means ..'s are applied before symlinks are resolved! */                                                                                                                          
+    if (!chabspath(junkptr))                                                                                                                                                                             
+       return 0;                                                                                                                                                                                         
+                                                                                                                                                                                                         
+    /* Notice that this means you cannot pass relative paths into this function! */                                                                                                                      
+    if (**junkptr != '/')                                                                                                                                                                                
+       return 0;                                                                                                                                                                                         
+                                                                                                                                                                                                         
+    char *lastpos = strend(*junkptr);                                                                                                                                                                    
+    char *nonreal = lastpos + 1;                                                                                                                                                                         
+    char real[PATH_MAX];                                                                                                                                                                                 
+                                                                                                                                                                                                         
+    while (!realpath(*junkptr, real)) {                                                                                                                                                                  
+       if (errno == EINVAL || errno == ELOOP || errno == ENAMETOOLONG || errno == ENOMEM)                                                                                                                
+           return 0;                                                                                                                                                                                     
+                                                                                                                                                                                                         
+       if (nonreal == *junkptr) {                                                                                                                                                                        
+           *real = '\0';                                                                                                                                                                                 
+           break;                                                                                                                                                                                        
+       }                                                                                                                                                                                                 
+                                                                                                                                                                                                         
+       while (*nonreal != '/' && nonreal >= *junkptr)                                                                                                                                                    
+           nonreal--;                                                                                                                                                                                    
+       *nonreal = '\0';                                                                                                                                                                                  
+    }                                                                                                                                                                                                    
+                                                                                                                                                                                                         
+    char *str = nonreal;                                                                                                                                                                                 
+    while (str <= lastpos) {                                                                                                                                                                             
+       if (*str == '\0')                                                                                                                                                                                 
+           *str = '/';                                                                                                                                                                                   
+       str++;                                                                                                                                                                                            
+    }                                                                                                                                                                                                    
+                                                                                                                                                                                                         
+    *junkptr = bicat(real, nonreal);                                                                                                                                                                     
+                                                                                                                                                                                                         
+    return 1;
+}
+
+/**/
+int
 remtpath(char **junkptr)
 {
     char *str = strend(*junkptr);
diff --git a/Src/subst.c b/Src/subst.c
index 9e3f06f..5033dd4 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -3199,6 +3199,8 @@ modify(char **str, char **ptr)

        for (; !c && **ptr;) {
            switch (**ptr) {
+            case 'a':
+            case 'A':
            case 'h':
            case 'r':
            case 'e':
@@ -3337,6 +3339,12 @@ modify(char **str, char **ptr)
                        copy = dupstring(tt);
                    *e = tc;
                    switch (c) {
+                    case 'a':
+                       chabspath(&copy);
+                       break;
+                   case 'A':
+                       chrealpath(&copy);
+                       break;
                    case 'h':
                        remtpath(&copy);
                        break;
@@ -3396,6 +3404,12 @@ modify(char **str, char **ptr)

            } else {
                switch (c) {
+               case 'a':
+                   chabspath(str);
+                   break;
+               case 'A':
+                   chrealpath(str);
+                   break;
                case 'h':
                    remtpath(str);
                    break;



      



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