various File::Glob fixes for DOSISH platforms
[p5sagit/p5-mst-13.2.git] / ext / File / Glob / bsd_glob.c
index f42b689..c422d60 100644 (file)
@@ -91,6 +91,9 @@ static char sccsid[] = "@(#)glob.c    8.3 (Berkeley) 10/13/93";
 #define        BG_RANGE        '-'
 #define        BG_RBRACKET     ']'
 #define        BG_SEP          '/'
+#ifdef DOSISH
+#define BG_SEP2                '\\'
+#endif
 #define        BG_STAR         '*'
 #define        BG_TILDE        '~'
 #define        BG_UNDERSCORE   '_'
@@ -132,6 +135,7 @@ typedef U8 Char;
 
 
 static int      compare(const void *, const void *);
+static int      ci_compare(const void *, const void *);
 static void     g_Ctoc(const Char *, char *);
 static int      g_lstat(Char *, Stat_t *, glob_t *);
 static DIR     *g_opendir(Char *, glob_t *);
@@ -148,7 +152,7 @@ static int   globextend(const Char *, glob_t *);
 static const Char *     globtilde(const Char *, Char *, glob_t *);
 static int      globexp1(const Char *, glob_t *);
 static int      globexp2(const Char *, const Char *, glob_t *, int *);
-static int      match(Char *, Char *, Char *);
+static int      match(Char *, Char *, Char *, int);
 #ifdef GLOB_DEBUG
 static void     qprintf(const char *, Char *);
 #endif /* GLOB_DEBUG */
@@ -186,11 +190,41 @@ bsd_glob(const char *pattern, int flags,
 
        bufnext = patbuf;
        bufend = bufnext + MAXPATHLEN;
+#ifdef DOSISH
+       /* Nasty hack to treat patterns like "C:*" correctly. In this
+        * case, the * should match any file in the current directory
+        * on the C: drive. However, the glob code does not treat the
+        * colon specially, so it looks for files beginning "C:" in
+        * the current directory. To fix this, change the pattern to
+        * add an explicit "./" at the start (just after the drive
+        * letter and colon - ie change to "C:./*").
+        */
+       if (isalpha(pattern[0]) && pattern[1] == ':' &&
+           pattern[2] != BG_SEP && pattern[2] != BG_SEP2 &&
+           bufend - bufnext > 4) {
+               *bufnext++ = pattern[0];
+               *bufnext++ = ':';
+               *bufnext++ = '.';
+               *bufnext++ = BG_SEP;
+               patnext += 2;
+       }
+#endif
        if (flags & GLOB_QUOTE) {
                /* Protect the quoted characters. */
                while (bufnext < bufend && (c = *patnext++) != BG_EOS)
                        if (c == BG_QUOTE) {
+#ifdef DOSISH
+                                   /* To avoid backslashitis on Win32,
+                                    * we only treat \ as a quoting character
+                                    * if it precedes one of the
+                                    * metacharacters []-{}~\
+                                    */
+                               if ((c = *patnext++) != '[' && c != ']' &&
+                                   c != '-' && c != '{' && c != '}' &&
+                                   c != '~' && c != '\\') {
+#else
                                if ((c = *patnext++) == BG_EOS) {
+#endif
                                        c = BG_QUOTE;
                                        --patnext;
                                }
@@ -496,12 +530,27 @@ glob0(const Char *pattern, glob_t *pglob)
         }
        else if (!(pglob->gl_flags & GLOB_NOSORT))
                qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,
-                   pglob->gl_pathc - oldpathc, sizeof(char *), compare);
+                   pglob->gl_pathc - oldpathc, sizeof(char *), 
+                   (pglob->gl_flags & GLOB_NOCASE) ? ci_compare : compare);
        pglob->gl_flags = oldflags;
        return(0);
 }
 
 static int
+ci_compare(const void *p, const void *q)
+{
+    const char *pp = *(const char **)p;
+    const char *qq = *(const char **)q;
+    while (*pp && *qq) {
+       if (tolower(*pp) != tolower(*qq))
+           break;
+       ++pp;
+       ++qq;
+    }
+    return (tolower(*pp) - tolower(*qq));
+}
+
+static int
 compare(const void *p, const void *q)
 {
        return(strcmp(*(char **)p, *(char **)q));
@@ -542,7 +591,11 @@ glob2(Char *pathbuf, Char *pathend, Char *pattern, glob_t *pglob)
                                return(0);
 
                        if (((pglob->gl_flags & GLOB_MARK) &&
-                           pathend[-1] != BG_SEP) && (S_ISDIR(sb.st_mode)
+                           pathend[-1] != BG_SEP
+#ifdef DOSISH
+                           && pathend[-1] != BG_SEP2
+#endif
+                           ) && (S_ISDIR(sb.st_mode)
                            || (S_ISLNK(sb.st_mode) &&
                            (g_stat(pathbuf, &sb, pglob) == 0) &&
                            S_ISDIR(sb.st_mode)))) {
@@ -559,7 +612,11 @@ glob2(Char *pathbuf, Char *pathend, Char *pattern, glob_t *pglob)
                /* Find end of next segment, copy tentatively to pathend. */
                q = pathend;
                p = pattern;
-               while (*p != BG_EOS && *p != BG_SEP) {
+               while (*p != BG_EOS && *p != BG_SEP
+#ifdef DOSISH
+                      && *p != BG_SEP2
+#endif
+                      ) {
                        if (ismeta(*p))
                                anymeta = 1;
                        *q++ = *p++;
@@ -568,7 +625,11 @@ glob2(Char *pathbuf, Char *pathend, Char *pattern, glob_t *pglob)
                if (!anymeta) {         /* No expansion, do next segment. */
                        pathend = q;
                        pattern = p;
-                       while (*pattern == BG_SEP)
+                       while (*pattern == BG_SEP
+#ifdef DOSISH
+                              || *pattern == BG_SEP2
+#endif
+                              )
                                *pathend++ = *pattern++;
                } else                  /* Need expansion, recurse. */
                        return(glob3(pathbuf, pathend, pattern, p, pglob));
@@ -583,6 +644,7 @@ glob3(Char *pathbuf, Char *pathend, Char *pattern,
        register Direntry_t *dp;
        DIR *dirp;
        int err;
+       int nocase;
        char buf[MAXPATHLEN];
 
        /*
@@ -608,6 +670,7 @@ glob3(Char *pathbuf, Char *pathend, Char *pattern,
        }
 
        err = 0;
+       nocase = ((pglob->gl_flags & GLOB_NOCASE) != 0);
 
        /* Search directory for matching names. */
        if (pglob->gl_flags & GLOB_ALTDIRFUNC)
@@ -624,7 +687,7 @@ glob3(Char *pathbuf, Char *pathend, Char *pattern,
                for (sc = (U8 *) dp->d_name, dc = pathend;
                     (*dc++ = *sc++) != BG_EOS;)
                        continue;
-               if (!match(pathend, pattern, restpattern)) {
+               if (!match(pathend, pattern, restpattern, nocase)) {
                        *pathend = BG_EOS;
                        continue;
                }
@@ -703,7 +766,7 @@ globextend(const Char *path, glob_t *pglob)
  * pattern causes a recursion level.
  */
 static int
-match(register Char *name, register Char *pat, register Char *patend)
+match(register Char *name, register Char *pat, register Char *patend, int nocase)
 {
        int ok, negate_range;
        Char c, k;
@@ -715,7 +778,7 @@ match(register Char *name, register Char *pat, register Char *patend)
                        if (pat == patend)
                                return(1);
                        do
-                           if (match(name, pat, patend))
+                           if (match(name, pat, patend, nocase))
                                    return(1);
                        while (*name++ != BG_EOS);
                        return(0);
@@ -731,16 +794,22 @@ match(register Char *name, register Char *pat, register Char *patend)
                                ++pat;
                        while (((c = *pat++) & M_MASK) != M_END)
                                if ((*pat & M_MASK) == M_RNG) {
-                                       if (c <= k && k <= pat[1])
-                                               ok = 1;
+                                       if (nocase) {
+                                               if (tolower(c) <= tolower(k) && tolower(k) <= tolower(pat[1]))
+                                                       ok = 1;
+                                       } else {
+                                               if (c <= k && k <= pat[1])
+                                                       ok = 1;
+                                       }
                                        pat += 2;
-                               } else if (c == k)
+                               } else if (nocase ? (tolower(c) == tolower(k)) : (c == k))
                                        ok = 1;
                        if (ok == negate_range)
                                return(0);
                        break;
                default:
-                       if (*name++ != c)
+                       k = *name++;
+                       if (nocase ? (tolower(k) != tolower(c)) : (k != c))
                                return(0);
                        break;
                }