X-Recipient: archive-cygwin AT delorie DOT com X-SWARE-Spam-Status: No, hits=-1.2 required=5.0 tests=AWL,BAYES_00,RP_MATCHES_RCVD,SPF_HELO_PASS,TW_FN,TW_NV,TW_VF X-Spam-Check-By: sourceware.org Message-ID: <50402422.1020901@malth.us> Date: Thu, 30 Aug 2012 19:40:34 -0700 From: "Gregory M. Turner" User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:14.0) Gecko/20120713 Thunderbird/14.0 MIME-Version: 1.0 To: cygwin AT cygwin DOT com Subject: [patch/rfc]: bash: fix globbing for executables without ".exe" Content-Type: multipart/mixed; boundary="------------040100040408000706070406" X-IsSubscribed: yes Mailing-List: contact cygwin-help AT cygwin DOT com; run by ezmlm List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: cygwin-owner AT cygwin DOT com Mail-Followup-To: cygwin AT cygwin DOT com Delivered-To: mailing list cygwin AT cygwin DOT com --------------040100040408000706070406 Content-Type: text/plain; charset=windows-1252; format=flowed Content-Transfer-Encoding: 7bit The attached attempts to mitigate a nasty interaction between globbing in bash and the cygwin .exe hack (i.e.: "/bin/b*sh" fails to match /bin/bash). I originally posted this (incorrectly) to the -apps ml. To be clear, this version is slightly revised from what I originally posted to -apps: specifically, I optimized the code selecting filenames matching "?*.exe" a bit, since this will often have to run nearly as many times as there are total files in all directories (nontrivially) searched by a given glob-pattern. -gmt --------------040100040408000706070406 Content-Type: text/plain; charset=windows-1252; name="bash-4.2_p36-cygwin-exe-globfix-r1.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="bash-4.2_p36-cygwin-exe-globfix-r1.patch" In cygwin, we have "the .exe hack" where both foo and foo.exe are treated as hardlinks to the same thing if foo is an executable or a symlink to an executable. This doesn't present a problem when globbing for something that matches "foo.exe" since readdir() always seems to tack the .exe on to the end. However, when globbing for "foo", we completely fail to find the file, for the same reason. The only thing for it is to explicitly check for this circumstance and inject a match into our results iff the glob matches "foo" but not "foo.exe". -gmt diff -urN bash-4.2.orig/lib/glob/glob.c bash-4.2/lib/glob/glob.c --- bash-4.2.orig/lib/glob/glob.c 2012-08-29 14:04:40.707465500 -0700 +++ bash-4.2/lib/glob/glob.c 2012-08-29 14:51:45.275465500 -0700 @@ -675,6 +675,103 @@ bcopy (dp->d_name, nextname, D_NAMLEN (dp) + 1); ++count; } +#if __CYGWIN__ + /* if file i.e. foo.exe is executable, see if "foo" matches the glob */ + else if (convfn[0] != '\0' && convfn[1] != '\0' && convfn[2] != '\0' && + convfn[3] != '\0' && convfn[4] != '\0') + { + char *x = &convfn[0]; + /* if sanity checks pass, assume this is the cygwin .exe hack at work. */ + struct stat finfo; + + while (*++x != '\0') ; + if (*--x == 'e' && *--x == 'x' && *--x == 'e' && *--x == '.') + { + subdir = sh_makepath (dir, dp->d_name, pflags); + if (!subdir) + { + lose = 1; + break; + } + if (stat (subdir, &finfo) == 0 && + ((S_ISREG (finfo.st_mode) || S_ISLNK (finfo.st_mode)) && + (finfo.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))) + { + size_t dnamlen = x - convfn; + /* We can kind-of abuse nextname here -- that's where it's + going, anyhow, if everything checks out OK */ + nextname = (char *) malloc (dnamlen + 1); + if (!nextname) + { + lose = 1; + break; + } + bcopy (dp->d_name, nextname, dnamlen); + *(nextname + dnamlen) = '\0'; + convfn = fnx_fromfs (nextname, dnamlen); + /* So, it's executable and ends in .exe -- does it match the + pattern, now that we have stripped the ".exe" off the end? */ + 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 */ + free(subdir); + subdir = sh_makepath (dir, convfn, 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 as we would get + stat()ing the link-target. The .exe hack does apply to such links! + I haven't tested various wierd corner cases. Guessing incorrectly + whether the .exe hack is operating will result in missed globs, or + (if we inject but then encounter the real file) duplicate matches */ + if (stat (subdir, &finfo) == 0 && + ((S_ISREG (finfo.st_mode) || S_ISLNK (finfo.st_mode)) && + (finfo.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))) + { + /* All signs point to cygwin .exe hack. Add it, it's 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); --------------040100040408000706070406 Content-Type: text/plain; charset=us-ascii -- Problem reports: http://cygwin.com/problems.html FAQ: http://cygwin.com/faq/ Documentation: http://cygwin.com/docs.html Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple --------------040100040408000706070406--