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);