Bug 332173 - Problems with regexp parsing of '~' in nsIZipReader.findEntries (and other nsWildCard uses). r=jwalden, a=ss
authorNelson Bolyard <nelson@bolyard.me>
Wed, 29 Jul 2009 12:10:30 -0700
changeset 26169 7a9fe7dfac8a00a54b7a8ab6bd618a70f1eb6fcf
parent 26168 039f168da2c880fa3687743c535ca774ed1537c7
child 26170 b8dbd5ab1647fc92b55ea9009744dbb9cc1e0005
child 26176 a83d5a98cdc67037c85683309a7c27f0efe9a8da
push id1839
push userjwalden@mit.edu
push dateThu, 30 Jul 2009 00:07:11 +0000
reviewersjwalden, ss
bugs332173
milestone1.9.1.2pre
Bug 332173 - Problems with regexp parsing of '~' in nsIZipReader.findEntries (and other nsWildCard uses). r=jwalden, a=ss
modules/libjar/nsIZipReader.idl
modules/libjar/nsWildCard.cpp
modules/libjar/nsWildCard.h
--- a/modules/libjar/nsIZipReader.idl
+++ b/modules/libjar/nsIZipReader.idl
@@ -13,25 +13,26 @@
  * for the specific language governing rights and limitations under the
  * License.
  *
  * The Original Code is Mozilla Communicator client code, released
  * March 31, 1998.
  *
  * The Initial Developer of the Original Code is
  * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
+ * Portions created by the Initial Developer are Copyright (C) 1998-2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Daniel Veditz <dveditz@netscape.com>
  *   Don Bragg <dbragg@netscape.com>
  *   Samir Gehani <sgehani@netscape.com>
  *   Mitch Stoltz <mstoltz@netscape.com>
  *   Jeff Walden <jwalden+code@mit.edu>
+ *   Nelson Bolyard <nelson@bolyard.me>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -138,47 +139,45 @@ interface nsIZipReader : nsISupports
      * Checks whether the zipfile contains an entry specified by entryName.
      */
     boolean hasEntry(in AUTF8String zipEntry);
  
     /**
      * Returns a string enumerator containing the matching entry names.
      *
      * @param aPattern
-     *   A regular expression used to find matching entries in the zip file.
+     *   A globbing pattern used to find matching names in the zip file.
      *   Set this parameter to null to get all entries; otherwise, use the
      *   following syntax:
      *
      *   o * matches anything
      *   o ? matches one character
      *   o $ matches the end of the string
-     *   o [abc] matches one occurrence of a, b, or c. The only character that
-     *           must be escaped inside the brackets is ].  ^ and - must never
-     *           appear in the first and second positions within the brackets, 
-     *           respectively.  (In the former case, the behavior specified for
-     *           '[^az]' will happen.)
-     *   o [a-z] matches any character between a and z.  The characters a and z
-     *           must either both be letters or both be numbers, with the
-     *           character represented by 'a' having a lower ASCII value than
-     *           the character represented by 'z'.
-     *   o [^az] matches any character except a or z.  If ] is to appear inside
-     *           the brackets as a character to not match, it must be escaped.
-     *   o pat~pat2 returns matches to the pattern 'pat' which do not also match
-     *              the pattern 'pat2'.  This may be used to perform filtering
-     *              upon the results of one pattern to remove all matches which
-     *              also match another pattern.  For example, because '*'
-     *              matches any string and '*z*' matches any string containing a
-     *              'z', '*~*z*' will match all strings except those containing
-     *              a 'z'.  Note that a pattern may not use '~' multiple times,
-     *              so a string such as '*~*z*~*y*' is not a valid pattern.
+     *   o [abc] matches one occurrence of a, b, or c. 
+     *   o [^az] matches any character except a or z.  Between brackets,
+     *           the only characters that must be escaped are \ and ].  
+     *   o [a-z] matches any character between a and z, inclusive.  
+     *           The a and z characters must be alphanumeric ASCII characters.
+     *           If one is upper case and one is lower case, then the ASCII
+     *           non-alphanumeric characters between Z and a will be in range.
+     *   o [^a-z] matches any character except those between a and z, inclusive.
+     *            These forms cannot be combined, e.g [a-gp-z] does not work.
+     *   o yes~no returns matches to the pattern 'yes' that do not also match
+     *            the pattern 'no'.  This may be used to filter the results
+     *            of one pattern to remove all matches of a second pattern.
+     *            Only the outer-most pattern may use this, and at most once.
+     *            For example: *~abc will match any string except abc .
      *   o (foo|bar) will match either the pattern foo or the pattern bar.
-     *               Neither of the patterns foo or bar may use the 'pat~pat2'
-     *               syntax described immediately above.
-     *   o \ will escape a special character.  Escaping is required for all
-     *       special characters unless otherwise specified.
+     *            At least one pipe and two inner patterns are required. 
+     *            More are allowed. These inner patterns may NOT use the 
+     *            'yes~no' syntax described immediately above, and may not 
+     *            contain patterns of this same (foo|bar) form.
+     *   o \ will escape a special character.  To treat special characters as
+     *            ordinary matching characters, escaping is required for all
+     *            special characters, unless otherwise specified above.
      *   o All other characters match case-sensitively.
      *
      *   An aPattern not conforming to this syntax has undefined behavior.
      *
      * @throws NS_ERROR_ILLEGAL_VALUE on many but not all invalid aPattern
      *                                values.
      */
     nsIUTF8StringEnumerator findEntries(in string aPattern);
--- a/modules/libjar/nsWildCard.cpp
+++ b/modules/libjar/nsWildCard.cpp
@@ -1,9 +1,8 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
  *
@@ -11,290 +10,400 @@
  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  * for the specific language governing rights and limitations under the
  * License.
  *
  * The Original Code is mozilla.org code.
  *
  * The Initial Developer of the Original Code is
  * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
+ * Portions created by the Initial Developer are Copyright (C) 1998-2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
+ *      Rob McCool  (original author)
+ *      Ken Key <key+mozilla@ksquared.net>
+ *      Nelson Bolyard <nelson@bolyard.me>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 /* *
- * 
  *
  * nsWildCard.cpp: shell-like wildcard match routines
  *
  * See nsIZipReader.findEntries documentation in nsIZipReader.idl for
  * a description of the syntax supported by the routines in this file.
  *
  * Rob McCool
- * 
+ *
  */
 
 #include "nsWildCard.h"
 #include "plstr.h"
 #include "prmem.h"
 
 /* ----------------------------- _valid_subexp ------------------------------ */
 
 
-static int 
-_valid_subexp(char *expr, char stop) 
+static int
+_valid_subexp(const char *expr, char stop1, char stop2)
 {
-    register int x,y,t;
-    int nsc,np,tld;
-
-    x=0;nsc=0;tld=0;
+    register int x;
+    int nsc = 0;     /* Number of special characters */
+    int np;          /* Number of pipe characters in union */
+    int tld = 0;     /* Number of tilde characters */
 
-    while(expr[x] && (expr[x] != stop)) {
+    for (x = 0; expr[x] && (expr[x] != stop1) && (expr[x] != stop2); ++x) {
         switch(expr[x]) {
-          case '~':
-            if(tld) return INVALID_SXP;
-            else ++tld;
-          case '*':
-          case '?':
-          case '^':
-          case '$':
+        case '~':
+            if(tld)                 /* at most one exclusion */
+                return INVALID_SXP;
+            if (stop1)              /* no exclusions within unions */
+                return INVALID_SXP;
+            if (!expr[x+1])          /* exclusion cannot be last character */
+                return INVALID_SXP;
+            if (!x)                 /* exclusion cannot be first character */
+                return INVALID_SXP;
+            ++tld;
+            /* fall through */
+        case '*':
+        case '?':
+        case '$':
             ++nsc;
             break;
-          case '[':
+        case '[':
             ++nsc;
             if((!expr[++x]) || (expr[x] == ']'))
                 return INVALID_SXP;
-            for(;expr[x] && (expr[x] != ']');++x)
-                if(expr[x] == '\\')
-                    if(!expr[++x])
-                        return INVALID_SXP;
+            for(; expr[x] && (expr[x] != ']'); ++x) {
+                if(expr[x] == '\\' && !expr[++x])
+                    return INVALID_SXP;
+            }
             if(!expr[x])
                 return INVALID_SXP;
             break;
-          case '(':
-            ++nsc;np = 0;
-            while(1) {
-                if(expr[++x] == ')')
-                    return INVALID_SXP;
-                for(y=x;(expr[y]) && (expr[y] != '|') && (expr[y] != ')');++y)
-                    if(expr[y] == '\\')
-                        if(!expr[++y])
-                            return INVALID_SXP;
-                if(!expr[y])
-                    return INVALID_SXP;
-                if(expr[y] == '|')
-                    ++np;
-                t = _valid_subexp(&expr[x],expr[y]);
-                if(t == INVALID_SXP)
+        case '(':
+            ++nsc;
+            if (stop1)                  /* no nested unions */
+                return INVALID_SXP;
+            np = -1;
+            do {
+                int t = _valid_subexp(&expr[++x], ')', '|');
+                if(t == 0 || t == INVALID_SXP)
                     return INVALID_SXP;
                 x+=t;
-                if(expr[x] == ')') {
-                    if(!np)
-                        return INVALID_SXP;
-                    break;
-                }
-            }
+                if(!expr[x])
+                    return INVALID_SXP;
+                ++np;
+            } while (expr[x] == '|' );
+            if(np < 1)  /* must be at least one pipe */
+                return INVALID_SXP;
             break;
-          case ')':
-          case ']':
+        case ')':
+        case ']':
+        case '|':
             return INVALID_SXP;
-          case '\\':
+        case '\\':
+            ++nsc;
             if(!expr[++x])
                 return INVALID_SXP;
-          default:
+            break;
+        default:
             break;
         }
-        ++x;
     }
-    if((!stop) && (!nsc))
+    if((!stop1) && (!nsc)) /* must be at least one special character */
         return NON_SXP;
-    return ((expr[x] == stop) ? x : INVALID_SXP);
+    return ((expr[x] == stop1 || expr[x] == stop2) ? x : INVALID_SXP);
 }
 
-int 
-NS_WildCardValid(char *expr) 
+
+int
+NS_WildCardValid(const char *expr)
 {
     int x;
 
-    x = _valid_subexp(expr, '\0');
+    x = _valid_subexp(expr, '\0', '\0');
     return (x < 0 ? x : VALID_SXP);
 }
 
 
 /* ----------------------------- _shexp_match ----------------------------- */
 
 
 #define MATCH 0
 #define NOMATCH 1
 #define ABORTED -1
 
-static int _shexp_match(char *str, char *expr, PRBool case_insensitive);
+static int
+_shexp_match(const char *str, const char *expr, PRBool case_insensitive,
+             unsigned int level);
+
+/* Count characters until we reach a NUL character or either of the
+ * two delimiter characters, stop1 or stop2.  If we encounter a bracketed
+ * expression, look only for NUL or ']' inside it.  Do not look for stop1
+ * or stop2 inside it. Return ABORTED if bracketed expression is unterminated.
+ * Handle all escaping.
+ * Return index in input string of first stop found, or ABORTED if not found.
+ * If "dest" is non-NULL, copy counted characters to it and NUL terminate.
+ */
+static int
+_scan_and_copy(const char *expr, char stop1, char stop2, char *dest)
+{
+    register int sx;     /* source index */
+    register char cc;
 
-static int 
-_handle_union(char *str, char *expr, PRBool case_insensitive) 
-{
-    char *e2 = (char *) PR_Malloc(sizeof(char)*strlen(expr));
-    register int t,p2,p1 = 1;
-    int cp;
+    for (sx = 0; (cc = expr[sx]) && cc != stop1 && cc != stop2; sx++) {
+        if (cc == '\\') {
+            if (!expr[++sx])
+                return ABORTED; /* should be impossible */
+        }
+        else if (cc == '[') {
+            while ((cc = expr[++sx]) && cc != ']') {
+                if(cc == '\\' && !expr[++sx])
+                    return ABORTED;
+            }
+            if (!cc)
+                return ABORTED; /* should be impossible */
+        }
+    }
+    if (dest && sx) {
+        /* Copy all but the closing delimiter. */
+        memcpy(dest, expr, sx);
+        dest[sx] = 0;
+    }
+    return cc ? sx : ABORTED; /* index of closing delimiter */
+}
 
-    while(1) {
-        for(cp=1;expr[cp] != ')';cp++)
-            if(expr[cp] == '\\')
-                ++cp;
-        for(p2 = 0;(expr[p1] != '|') && (p1 != cp);p1++,p2++) {
-            if(expr[p1] == '\\')
-                e2[p2++] = expr[p1++];
-            e2[p2] = expr[p1];
+/* On input, expr[0] is the opening parenthesis of a union.
+ * See if any of the alternatives in the union matches as a pattern.
+ * The strategy is to take each of the alternatives, in turn, and append
+ * the rest of the expression (after the closing ')' that marks the end of
+ * this union) to that alternative, and then see if the resultant expression
+ * matches the input string.  Repeat this until some alternative matches,
+ * or we have an abort.
+ */
+static int
+_handle_union(const char *str, const char *expr, PRBool case_insensitive,
+              unsigned int level)
+{
+    register int sx;     /* source index */
+    int cp;              /* source index of closing parenthesis */
+    int count;
+    int ret   = NOMATCH;
+    char *e2;
+
+    /* Find the closing parenthesis that ends this union in the expression */
+    cp = _scan_and_copy(expr, ')', '\0', NULL);
+    if (cp == ABORTED || cp < 4) /* must be at least "(a|b" before ')' */
+        return ABORTED;
+    ++cp;                /* now index of char after closing parenthesis */
+    e2 = (char *) PR_Malloc(1 + strlen(expr));
+    if (!e2)
+        return ABORTED;
+    for (sx = 1; ; ++sx) {
+        /* Here, expr[sx] is one character past the preceeding '(' or '|'. */
+        /* Copy everything up to the next delimiter to e2 */
+        count = _scan_and_copy(expr + sx, ')', '|', e2);
+        if (count == ABORTED || !count) {
+            ret = ABORTED;
+            break;
         }
-        for (t=cp+1; ((e2[p2] = expr[t]) != 0); ++t,++p2) {}
-        if(_shexp_match(str,e2, case_insensitive) == MATCH) {
-            PR_Free(e2);
-            return MATCH;
-        }
-        if(p1 == cp) {
-            PR_Free(e2);
+        sx += count;
+        /* Append everything after closing parenthesis to e2. This is safe. */
+        strcpy(e2+count, expr+cp);
+        ret = _shexp_match(str, e2, case_insensitive, level + 1);
+        if (ret != NOMATCH || !expr[sx] || expr[sx] == ')')
+            break;
+    }
+    PR_Free(e2);
+    if (sx < 2)
+        ret = ABORTED;
+    return ret;
+}
+
+/* returns 1 if val is in range from start..end, case insensitive. */
+static int
+_is_char_in_range(int start, int end, int val)
+{
+    char map[256];
+    memset(map, 0, sizeof map);
+    while (start <= end)
+        map[tolower(start++)] = 1;
+    return map[tolower(val)];
+}
+
+static int
+_shexp_match(const char *str, const char *expr, PRBool case_insensitive,
+             unsigned int level)
+{
+    register int x;   /* input string index */
+    register int y;   /* expression index */
+    int ret,neg;
+
+    if (level > 20)      /* Don't let the stack get too deep. */
+        return ABORTED;
+    for(x = 0, y = 0; expr[y]; ++y, ++x) {
+        if((!str[x]) && (expr[y] != '$') && (expr[y] != '*')) {
             return NOMATCH;
         }
-        else ++p1;
+        switch(expr[y]) {
+        case '$':
+            if(str[x])
+                return NOMATCH;
+            --x;                 /* we don't want loop to increment x */
+            break;
+        case '*':
+            while(expr[++y] == '*'){}
+            if(!expr[y])
+                return MATCH;
+            while(str[x]) {
+                ret = _shexp_match(&str[x++], &expr[y], case_insensitive,
+                                   level + 1);
+                switch(ret) {
+                case NOMATCH:
+                    continue;
+                case ABORTED:
+                    return ABORTED;
+                default:
+                    return MATCH;
+                }
+            }
+            if((expr[y] == '$') && (expr[y+1] == '\0') && (!str[x]))
+                return MATCH;
+            else
+                return NOMATCH;
+        case '[': {
+            int start, end = 0, i;
+            neg = ((expr[++y] == '^') && (expr[y+1] != ']'));
+            if (neg)
+                ++y;
+            i = y;
+            start = (unsigned char)(expr[i++]);
+            if (start == '\\')
+                start = (unsigned char)(expr[i++]);
+            if (isalnum(start) && expr[i++] == '-') {
+                end = (unsigned char)(expr[i++]);
+                if (end == '\\')
+                    end = (unsigned char)(expr[i++]);
+            }
+            if (isalnum(end) && expr[i] == ']') {
+                /* This is a range form: a-b */
+                int val   = (unsigned char)(str[x]);
+                if (end < start) { /* swap them */
+                    int tmp = end;
+                    end = start;
+                    start = tmp;
+                }
+                if (case_insensitive && isalpha(val)) {
+                    val = _is_char_in_range(start, end, val);
+                    if (neg == val)
+                        return NOMATCH;
+                }
+                else if (neg != ((val < start) || (val > end))) {
+                    return NOMATCH;
+                }
+                y = i;
+            }
+            else {
+                /* Not range form */
+                int matched = 0;
+                for (; expr[y] != ']'; y++) {
+                    if (expr[y] == '\\')
+                        ++y;
+                    if(case_insensitive) {
+                        matched |= (toupper(str[x]) == toupper(expr[y]));
+                    }
+                    else {
+                        matched |= (str[x] == expr[y]);
+                    }
+                }
+                if (neg == matched)
+                    return NOMATCH;
+            }
+        }
+        break;
+        case '(':
+            if (!expr[y+1])
+                return ABORTED;
+            return _handle_union(&str[x], &expr[y], case_insensitive, level + 1);
+        case '?':
+            break;
+        case ')':
+        case ']':
+        case '|':
+            return ABORTED;
+        case '\\':
+            ++y;
+            /* fall through */
+        default:
+            if(case_insensitive) {
+                if(toupper(str[x]) != toupper(expr[y]))
+                    return NOMATCH;
+            }
+            else {
+                if(str[x] != expr[y])
+                    return NOMATCH;
+            }
+            break;
+        }
     }
+    return (str[x] ? NOMATCH : MATCH);
+}
+
+static int
+ns_WildCardMatch(const char *str, const char *xp, PRBool case_insensitive)
+{
+    char *expr = 0;
+    int x, ret = MATCH;
+
+    if (!strchr(xp, '~'))
+        return _shexp_match(str, xp, case_insensitive, 0);
+
+    expr = PL_strdup(xp);
+    if(!expr)
+        return NOMATCH;
+
+    x = _scan_and_copy(expr, '~', '\0', NULL);
+    if (x != ABORTED && expr[x] == '~') {
+        expr[x++] = '\0';
+        ret = _shexp_match(str, &expr[x], case_insensitive, 0);
+        switch (ret) {
+        case NOMATCH: ret = MATCH;   break;
+        case MATCH:   ret = NOMATCH; break;
+        default:                     break;
+        }
+    }
+    if (ret == MATCH)
+        ret = _shexp_match(str, expr, case_insensitive, 0);
+
+    PR_Free(expr);
+    return ret;
 }
 
 
-static int 
-_shexp_match(char *str, char *expr, PRBool case_insensitive) 
+int
+NS_WildCardMatch(const char *str, const char *expr, PRBool case_insensitive)
 {
-    register int x,y;
-    int ret,neg;
-
-    ret = 0;
-    for(x=0,y=0;expr[y];++y,++x) {
-        if((!str[x]) && (expr[y] != '(') && (expr[y] != '$') && (expr[y] != '*'))
-            ret = ABORTED;
-        else {
-            switch(expr[y]) {
-              case '$':
-                if( (str[x]) )
-                    ret = NOMATCH;
-                else
-                    --x;             /* we don't want loop to increment x */
-                break;
-              case '*':
-                while(expr[++y] == '*'){}
-                if(!expr[y])
-                    return MATCH;
-                while(str[x]) {
-                    switch(_shexp_match(&str[x++],&expr[y], case_insensitive)) {
-                    case NOMATCH:
-                        continue;
-                    case ABORTED:
-                        ret = ABORTED;
-                        break;
-                    default:
-                        return MATCH;
-                    }
-                    break;
-                }
-                if((expr[y] == '$') && (expr[y+1] == '\0') && (!str[x]))
-                    return MATCH;
-                else
-                    ret = ABORTED;
-                break;
-              case '[':
-              	neg = ((expr[++y] == '^') && (expr[y+1] != ']'));
-                if (neg)
-                    ++y;
-                
-                if ((isalnum(expr[y])) && (expr[y+1] == '-') && 
-                   (isalnum(expr[y+2])) && (expr[y+3] == ']'))
-                    {
-                        int start = expr[y], end = expr[y+2];
-                        
-                        /* Droolproofing for pinheads not included */
-                        if(neg ^ ((str[x] < start) || (str[x] > end))) {
-                            ret = NOMATCH;
-                            break;
-                        }
-                        y+=3;
-                    }
-                else {
-                    int matched;
-                    
-                    for (matched=0;expr[y] != ']';y++) {
-                        /* match an escaped ']' character */
-                        if('\\' == expr[y] && ']' == expr[y+1]) {
-                            if(']' == str[x])
-                                matched |= 1;
-                            y++; /* move an extra char to compensate for '\\' */
-                            continue;
-                        }
-                        matched |= (str[x] == expr[y]);
-                    }
-                    if (neg ^ (!matched))
-                        ret = NOMATCH;
-                }
-                break;
-              case '(':
-                return _handle_union(&str[x],&expr[y], case_insensitive);
-                break;
-              case '?':
-                break;
-              case '\\':
-                ++y;
-              default:
-				if(case_insensitive)
-				  {
-                    if(toupper(str[x]) != toupper(expr[y]))
-                        ret = NOMATCH;
-				  }
-				else
-				  {
-                    if(str[x] != expr[y])
-                        ret = NOMATCH;
-				  }
-                break;
-            }
-        }
-        if(ret)
-            break;
+    int is_valid = NS_WildCardValid(expr);
+    switch(is_valid) {
+        case INVALID_SXP:
+            return -1;
+        case NON_SXP:
+            if (case_insensitive)
+                return (PL_strcasecmp(expr,str) ? NOMATCH : MATCH);
+            return (strcmp(expr,str) ? NOMATCH : MATCH);
+        default:
+            return ns_WildCardMatch(str, expr, case_insensitive);
     }
-    return (ret ? ret : (str[x] ? NOMATCH : MATCH));
 }
-
-int 
-NS_WildCardMatch(char *str, char *xp, PRBool case_insensitive) {
-    register int x;
-    char *expr = PL_strdup(xp);
-
-	if(!expr)
-		return 1;
-
-    for(x=strlen(expr)-1;x;--x) {
-        if((expr[x] == '~') && (expr[x-1] != '\\')) {
-            expr[x] = '\0';
-            if(_shexp_match(str,&expr[++x], case_insensitive) == MATCH)
-                goto punt;
-            break;
-        }
-    }
-    if(_shexp_match(str,expr, case_insensitive) == MATCH) {
-        PR_Free(expr);
-        return 0;
-    }
-
-  punt:
-    PR_Free(expr);
-    return 1;
-}
--- a/modules/libjar/nsWildCard.h
+++ b/modules/libjar/nsWildCard.h
@@ -11,20 +11,21 @@
  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  * for the specific language governing rights and limitations under the
  * License.
  *
  * The Original Code is mozilla.org code.
  *
  * The Initial Developer of the Original Code is
  * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
+ * Portions created by the Initial Developer are Copyright (C) 1998-2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
+ *      Nelson Bolyard <nelson@bolyard.me>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -66,27 +67,28 @@
  *  INVALID_SXP  if exp is a shell expression, but invalid
  *  VALID_SXP    if exp is a valid shell expression
  */
 
 #define NON_SXP -1
 #define INVALID_SXP -2
 #define VALID_SXP 1
 
-extern int NS_WildCardValid(char *expr);
+extern int NS_WildCardValid(const char *expr);
 
 
 /* return values for the search routines */
 #define MATCH 0
 #define NOMATCH 1
 #define ABORTED -1
 
 /*
  * NS_WildCardMatch
  * 
  * Takes a prevalidated shell expression exp, and a string str.
  *
  * Returns 0 on match and 1 on non-match.
  */
 
-extern int NS_WildCardMatch(char *str, char *expr, PRBool case_insensitive);
+extern int 
+NS_WildCardMatch(const char *str, const char *expr, PRBool case_insensitive);
 
 #endif /* nsWildCard_h__ */