On 9/9/2012 6:52 PM, John E. Malmberg wrote:
On 9/9/2012 7:52 PM, Gregory M. Turner wrote:
>>
I'd be happy to iterate if folks have some good ideas as to how to
improve the implementation.

Don't forget that the file test operators also need to be able to find
the same binary files.  Otherwise you can run into a situation where you
can see the file with glob, but not see the file when you try to test to
see if it exist or to stat it.

It already tested for that -- but, now that you mention it, if foo was a symlink and foo.exe was a regular file, it was returning a match -- in such a case, the cygwin .exe-hack couldn't possibly be responsible for foo's presence. I've enclosed an updated patch fixing that.

> [snip]

I have not yet found anything that actually cares so far though.

Of course, I found a real-world need, or I wouldn't have bothered (some Gentoo scripts that were going to be difficult to cygwin-ify otherwise).

-gmt
commit f2c57536fd18179abc8d65708b2142b5060e0101
Author: Gregory M. Turner <gmturner...@ameritech.net>
Date:   Sat Sep 8 22:05:06 2012 -0700

    lib/glob: cygwin: match executables without ".exe" suffix
    
    On cygwin we have the ".exe-hack" which is a feature that
    attempts to make the Windowsy ".exe" suffix on executables
    optional by acting (almost) as though "foo" and "foo.exe" were
    hard-links to the same file.
    
    The "almost" part is that when enumarating a directory's
    contents, only the ".exe"-suffixed version is returned.
    As a result, bash has historically failed to match globs
    which don't match the ".exe" suffix, even though, for all
    intents and purposes, the file is there.  For example,
    
      echo /bin/ba?h
    
    would not find the /bin/bash executable.
    
    This patch fixes this by keeping an eye out for files that fail to
    match the glob but do look like they'd match "*?.exe".
    
    When such a file is encountered, some sanity checks are
    performed, and if the ".exe-hack" seems applicable, the
    ".exe" suffix is removed and the glob is checked against
    the suffix-free version of the filename, before rejection.
    Such matches are returned without the .exe suffix (as, it
    would be quite unexpected to get a result back from globbing
    that doesn't match the glob!).
    
    Signed-off-by: Gregory M. Turner <gmturner...@ameritech.net>

diff --git a/lib/glob/glob.c b/lib/glob/glob.c
index ad9b9d9..97e1f4e 100644
--- a/lib/glob/glob.c
+++ b/lib/glob/glob.c
@@ -675,6 +675,108 @@ glob_vector (pat, dir, flags)
              bcopy (dp->d_name, nextname, D_NAMLEN (dp) + 1);
              ++count;
            }
+#if __CYGWIN__
+         /* The master plan here is to check whether "foo" matches the glob 
when the
+            above has just ruled out a match for the executable or 
symlink-to-executable
+            "foo.exe".  If it does match, we add "foo" as though we got it 
from readdir.
+
+            Before we leap, though, we should look a bit.  Incorrectly 
guessing whether
+            the .exe hack applies would result in missed globs, or (if we 
inject "mccoy"
+            but then encounter the real "mccoy") duplicate matches; hopefully 
this is
+            never wrong in practice -- if it becomes an issue, we should 
adjust our
+            sanity checking to better capture the conditions actually used by 
cygwin. */
+
+         /* We aren't interested in anything less than five characters long 
(what are
+            we going to match for ".exe," an empty string?) */
+         else if (convfn[0] != '\0' && convfn[1] != '\0' && convfn[2] != '\0' 
&&
+                  convfn[3] != '\0' && convfn[4] != '\0')
+           {
+             register char *x = &convfn[0];
+             struct stat f_exe_info, f_info;
+             while (*++x != '\0') ;
+             if (*--x == 'e' && *--x == 'x' && *--x == 'e' && *--x == '.')
+               {
+
+                 /* abuse the available register char* 'subdir' to build the 
path */
+                 subdir = sh_makepath (dir, dp->d_name, pflags);
+                 if (!subdir)
+                   {
+                     lose = 1;
+                     break;
+                   }
+                 /* My tests indicate that we can stat() the executable bit of 
a valid
+                    symlink in cygwin, expecting to get the same result we 
would get
+                    stat()ing the link-target.  The .exe hack does apply to 
such links! */
+                 if (stat (subdir, &f_exe_info) == 0 &&
+                     ((S_ISREG (f_exe_info.st_mode) || S_ISLNK 
(f_exe_info.st_mode)) &&
+                      (f_exe_info.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))))
+                   {
+                     /* So, it's an executable or symlink thereto and ends in 
.exe -- does
+                        it match the pattern if we strip the ".exe" suffix? */
+                     size_t dnamlen = x - convfn; /* x points to the last char 
before '.exe' */
+                     nextname = (char *) malloc (dnamlen + 1);
+                     if (!nextname)
+                       {
+                         lose = 1;
+                         break;
+                       }
+                     bcopy (dp->d_name, nextname, dnamlen);   /* always a noop 
on cygwin */
+                     *(nextname + dnamlen) = '\0';
+                     convfn = fnx_fromfs (nextname, dnamlen);
+                     if (strmatch (pat, convfn, mflags) != FNM_NOMATCH)
+                       {
+                         /* Great, it matches, but does it actually exist, and 
is it executable?
+                            Probably, yes.  This protects against some future 
cygwin that made
+                            the .exe hack optional or removed it, and against 
the possibility that
+                            I've failed to capture the conditions that trigger 
the .exe hack in
+                            general.  A better test might be to check if they 
have the same inode,
+                            but this might require some nuance due to 
inode-reliability quirks */
+                         free(subdir);
+                         subdir = sh_makepath (dir, convfn, pflags);
+                         if (!subdir)
+                           {
+                             lose = 1;
+                             break;
+                           }
+                         if (stat (subdir, &f_info) == 0 && f_info.st_mode == 
f_exe_info.st_mode)
+                           {
+                             /* All signs point to the cygwin .exe hack.  
Treat it as a match */
+                             if (nalloca < ALLOCA_MAX)
+                               {
+                                 nextlink = (struct globval *) alloca (sizeof 
(struct globval));
+                                 nalloca += sizeof (struct globval);
+                               }
+                             else
+                               {
+                                 nextlink = (struct globval *) malloc (sizeof 
(struct globval));
+                                 if (firstmalloc == 0)
+                                 firstmalloc = nextlink;
+                               }
+
+                             if (!nextlink)
+                               {
+                                 lose = 1;
+                                 break;
+                               }
+                             nextlink->next = lastlink;
+                             lastlink = nextlink;
+                             nextlink->name = nextname;
+                             ++count;
+                           }
+                         else
+                           {
+                             free(nextname);
+                           }
+                       }
+                     else
+                       {
+                         free (nextname);
+                       }
+                   }
+                 free(subdir);
+               }
+           }
+#endif
        }
 
       (void) closedir (d);

Reply via email to