LCOV - code coverage report
Current view: top level - xpcom/io - nsWildCard.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1 215 0.5 %
Date: 2017-07-14 16:53:18 Functions: 2 28 7.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : /* *
       8             :  *
       9             :  *
      10             :  * nsWildCard.cpp: shell-like wildcard match routines
      11             :  *
      12             :  * See nsIZipReader.findEntries documentation in nsIZipReader.idl for
      13             :  * a description of the syntax supported by the routines in this file.
      14             :  *
      15             :  * Rob McCool
      16             :  *
      17             :  */
      18             : 
      19             : #include "nsWildCard.h"
      20             : #include "nsXPCOM.h"
      21             : #include "nsCRTGlue.h"
      22             : #include "nsCharTraits.h"
      23             : 
      24             : /* -------------------- ASCII-specific character methods ------------------- */
      25             : 
      26             : typedef int static_assert_character_code_arrangement['a' > 'A' ? 1 : -1];
      27             : 
      28             : template<class T>
      29             : static int
      30           0 : alpha(T aChar)
      31             : {
      32           0 :   return ('a' <= aChar && aChar <= 'z') ||
      33           0 :          ('A' <= aChar && aChar <= 'Z');
      34             : }
      35             : 
      36             : template<class T>
      37             : static int
      38           0 : alphanumeric(T aChar)
      39             : {
      40           0 :   return ('0' <= aChar && aChar <= '9') || ::alpha(aChar);
      41             : }
      42             : 
      43             : template<class T>
      44             : static int
      45           0 : lower(T aChar)
      46             : {
      47           0 :   return ('A' <= aChar && aChar <= 'Z') ? aChar + ('a' - 'A') : aChar;
      48             : }
      49             : 
      50             : template<class T>
      51             : static int
      52           0 : upper(T aChar)
      53             : {
      54           0 :   return ('a' <= aChar && aChar <= 'z') ? aChar - ('a' - 'A') : aChar;
      55             : }
      56             : 
      57             : /* ----------------------------- _valid_subexp ---------------------------- */
      58             : 
      59             : template<class T>
      60             : static int
      61           0 : _valid_subexp(const T* aExpr, T aStop1, T aStop2)
      62             : {
      63             :   int x;
      64           0 :   int nsc = 0;     /* Number of special characters */
      65             :   int np;          /* Number of pipe characters in union */
      66           0 :   int tld = 0;     /* Number of tilde characters */
      67             : 
      68           0 :   for (x = 0; aExpr[x] && (aExpr[x] != aStop1) && (aExpr[x] != aStop2); ++x) {
      69           0 :     switch (aExpr[x]) {
      70             :       case '~':
      71           0 :         if (tld) {              /* at most one exclusion */
      72           0 :           return INVALID_SXP;
      73             :         }
      74           0 :         if (aStop1) {           /* no exclusions within unions */
      75           0 :           return INVALID_SXP;
      76             :         }
      77           0 :         if (!aExpr[x + 1]) {    /* exclusion cannot be last character */
      78           0 :           return INVALID_SXP;
      79             :         }
      80           0 :         if (!x) {               /* exclusion cannot be first character */
      81           0 :           return INVALID_SXP;
      82             :         }
      83           0 :         ++tld;
      84             :         MOZ_FALLTHROUGH;
      85             :       case '*':
      86             :       case '?':
      87             :       case '$':
      88           0 :         ++nsc;
      89           0 :         break;
      90             :       case '[':
      91           0 :         ++nsc;
      92           0 :         if ((!aExpr[++x]) || (aExpr[x] == ']')) {
      93           0 :           return INVALID_SXP;
      94             :         }
      95           0 :         for (; aExpr[x] && (aExpr[x] != ']'); ++x) {
      96           0 :           if (aExpr[x] == '\\' && !aExpr[++x]) {
      97           0 :             return INVALID_SXP;
      98             :           }
      99             :         }
     100           0 :         if (!aExpr[x]) {
     101           0 :           return INVALID_SXP;
     102             :         }
     103           0 :         break;
     104             :       case '(':
     105           0 :         ++nsc;
     106           0 :         if (aStop1) {           /* no nested unions */
     107           0 :           return INVALID_SXP;
     108             :         }
     109           0 :         np = -1;
     110           0 :         do {
     111           0 :           int t = ::_valid_subexp(&aExpr[++x], T(')'), T('|'));
     112           0 :           if (t == 0 || t == INVALID_SXP) {
     113           0 :             return INVALID_SXP;
     114             :           }
     115           0 :           x += t;
     116           0 :           if (!aExpr[x]) {
     117           0 :             return INVALID_SXP;
     118             :           }
     119           0 :           ++np;
     120           0 :         } while (aExpr[x] == '|');
     121           0 :         if (np < 1) { /* must be at least one pipe */
     122           0 :           return INVALID_SXP;
     123             :         }
     124           0 :         break;
     125             :       case ')':
     126             :       case ']':
     127             :       case '|':
     128           0 :         return INVALID_SXP;
     129             :       case '\\':
     130           0 :         ++nsc;
     131           0 :         if (!aExpr[++x]) {
     132           0 :           return INVALID_SXP;
     133             :         }
     134           0 :         break;
     135             :       default:
     136           0 :         break;
     137             :     }
     138             :   }
     139           0 :   if (!aStop1 && !nsc) { /* must be at least one special character */
     140           0 :     return NON_SXP;
     141             :   }
     142           0 :   return ((aExpr[x] == aStop1 || aExpr[x] == aStop2) ? x : INVALID_SXP);
     143             : }
     144             : 
     145             : 
     146             : template<class T>
     147             : int
     148           0 : NS_WildCardValid_(const T* aExpr)
     149             : {
     150           0 :   int x = ::_valid_subexp(aExpr, T('\0'), T('\0'));
     151           0 :   return (x < 0 ? x : VALID_SXP);
     152             : }
     153             : 
     154             : int
     155           0 : NS_WildCardValid(const char* aExpr)
     156             : {
     157           0 :   return NS_WildCardValid_(aExpr);
     158             : }
     159             : 
     160             : int
     161           0 : NS_WildCardValid(const char16_t* aExpr)
     162             : {
     163           0 :   return NS_WildCardValid_(aExpr);
     164             : }
     165             : 
     166             : /* ----------------------------- _shexp_match ----------------------------- */
     167             : 
     168             : 
     169             : #define MATCH 0
     170             : #define NOMATCH 1
     171             : #define ABORTED -1
     172             : 
     173             : template<class T>
     174             : static int
     175             : _shexp_match(const T* aStr, const T* aExpr, bool aCaseInsensitive,
     176             :              unsigned int aLevel);
     177             : 
     178             : /**
     179             :  * Count characters until we reach a NUL character or either of the
     180             :  * two delimiter characters, stop1 or stop2.  If we encounter a bracketed
     181             :  * expression, look only for NUL or ']' inside it.  Do not look for stop1
     182             :  * or stop2 inside it. Return ABORTED if bracketed expression is unterminated.
     183             :  * Handle all escaping.
     184             :  * Return index in input string of first stop found, or ABORTED if not found.
     185             :  * If "dest" is non-nullptr, copy counted characters to it and null terminate.
     186             :  */
     187             : template<class T>
     188             : static int
     189           0 : _scan_and_copy(const T* aExpr, T aStop1, T aStop2, T* aDest)
     190             : {
     191             :   int sx;     /* source index */
     192             :   T cc;
     193             : 
     194           0 :   for (sx = 0; (cc = aExpr[sx]) && cc != aStop1 && cc != aStop2; ++sx) {
     195           0 :     if (cc == '\\') {
     196           0 :       if (!aExpr[++sx]) {
     197           0 :         return ABORTED;  /* should be impossible */
     198             :       }
     199           0 :     } else if (cc == '[') {
     200           0 :       while ((cc = aExpr[++sx]) && cc != ']') {
     201           0 :         if (cc == '\\' && !aExpr[++sx]) {
     202           0 :           return ABORTED;
     203             :         }
     204             :       }
     205           0 :       if (!cc) {
     206           0 :         return ABORTED;  /* should be impossible */
     207             :       }
     208             :     }
     209             :   }
     210           0 :   if (aDest && sx) {
     211             :     /* Copy all but the closing delimiter. */
     212           0 :     memcpy(aDest, aExpr, sx * sizeof(T));
     213           0 :     aDest[sx] = 0;
     214             :   }
     215           0 :   return cc ? sx : ABORTED; /* index of closing delimiter */
     216             : }
     217             : 
     218             : /* On input, expr[0] is the opening parenthesis of a union.
     219             :  * See if any of the alternatives in the union matches as a pattern.
     220             :  * The strategy is to take each of the alternatives, in turn, and append
     221             :  * the rest of the expression (after the closing ')' that marks the end of
     222             :  * this union) to that alternative, and then see if the resultant expression
     223             :  * matches the input string.  Repeat this until some alternative matches,
     224             :  * or we have an abort.
     225             :  */
     226             : template<class T>
     227             : static int
     228           0 : _handle_union(const T* aStr, const T* aExpr, bool aCaseInsensitive,
     229             :               unsigned int aLevel)
     230             : {
     231             :   int sx;              /* source index */
     232             :   int cp;              /* source index of closing parenthesis */
     233             :   int count;
     234           0 :   int ret   = NOMATCH;
     235             :   T* e2;
     236             : 
     237             :   /* Find the closing parenthesis that ends this union in the expression */
     238           0 :   cp = ::_scan_and_copy(aExpr, T(')'), T('\0'), static_cast<T*>(nullptr));
     239           0 :   if (cp == ABORTED || cp < 4) { /* must be at least "(a|b" before ')' */
     240           0 :     return ABORTED;
     241             :   }
     242           0 :   ++cp;                /* now index of char after closing parenthesis */
     243           0 :   e2 = (T*)moz_xmalloc((1 + nsCharTraits<T>::length(aExpr)) * sizeof(T));
     244           0 :   if (!e2) {
     245           0 :     return ABORTED;
     246             :   }
     247           0 :   for (sx = 1; ; ++sx) {
     248             :     /* Here, aExpr[sx] is one character past the preceding '(' or '|'. */
     249             :     /* Copy everything up to the next delimiter to e2 */
     250           0 :     count = ::_scan_and_copy(aExpr + sx, T(')'), T('|'), e2);
     251           0 :     if (count == ABORTED || !count) {
     252           0 :       ret = ABORTED;
     253           0 :       break;
     254             :     }
     255           0 :     sx += count;
     256             :     /* Append everything after closing parenthesis to e2. This is safe. */
     257           0 :     nsCharTraits<T>::copy(e2 + count, aExpr + cp,
     258           0 :                           nsCharTraits<T>::length(aExpr + cp) + 1);
     259           0 :     ret = ::_shexp_match(aStr, e2, aCaseInsensitive, aLevel + 1);
     260           0 :     if (ret != NOMATCH || !aExpr[sx] || aExpr[sx] == ')') {
     261             :       break;
     262             :     }
     263             :   }
     264           0 :   free(e2);
     265           0 :   if (sx < 2) {
     266           0 :     ret = ABORTED;
     267             :   }
     268           0 :   return ret;
     269             : }
     270             : 
     271             : /* returns 1 if val is in range from start..end, case insensitive. */
     272             : static int
     273           0 : _is_char_in_range(unsigned char aStart, unsigned char aEnd, unsigned char aVal)
     274             : {
     275             :   char map[256];
     276           0 :   memset(map, 0, sizeof(map));
     277           0 :   while (aStart <= aEnd) {
     278           0 :     map[lower(aStart++)] = 1;
     279             :   }
     280           0 :   return map[lower(aVal)];
     281             : }
     282             : 
     283             : template<class T>
     284             : static int
     285           0 : _shexp_match(const T* aStr, const T* aExpr, bool aCaseInsensitive,
     286             :              unsigned int aLevel)
     287             : {
     288             :   int x;   /* input string index */
     289             :   int y;   /* expression index */
     290             :   int ret, neg;
     291             : 
     292           0 :   if (aLevel > 20) {    /* Don't let the stack get too deep. */
     293           0 :     return ABORTED;
     294             :   }
     295           0 :   for (x = 0, y = 0; aExpr[y]; ++y, ++x) {
     296           0 :     if (!aStr[x] && aExpr[y] != '$' && aExpr[y] != '*') {
     297           0 :       return NOMATCH;
     298             :     }
     299           0 :     switch (aExpr[y]) {
     300             :       case '$':
     301           0 :         if (aStr[x]) {
     302           0 :           return NOMATCH;
     303             :         }
     304           0 :         --x;                 /* we don't want loop to increment x */
     305           0 :         break;
     306             :       case '*':
     307           0 :         while (aExpr[++y] == '*') {
     308             :         }
     309           0 :         if (!aExpr[y]) {
     310           0 :           return MATCH;
     311             :         }
     312           0 :         while (aStr[x]) {
     313           0 :           ret = ::_shexp_match(&aStr[x++], &aExpr[y], aCaseInsensitive,
     314             :                                aLevel + 1);
     315           0 :           switch (ret) {
     316             :             case NOMATCH:
     317           0 :               continue;
     318             :             case ABORTED:
     319           0 :               return ABORTED;
     320             :             default:
     321           0 :               return MATCH;
     322             :           }
     323             :         }
     324           0 :         if (aExpr[y] == '$' && aExpr[y + 1] == '\0' && !aStr[x]) {
     325           0 :           return MATCH;
     326             :         } else {
     327           0 :           return NOMATCH;
     328             :         }
     329             :       case '[': {
     330           0 :         T start, end = 0;
     331             :         int i;
     332           0 :         ++y;
     333           0 :         neg = (aExpr[y] == '^' && aExpr[y + 1] != ']');
     334           0 :         if (neg) {
     335           0 :           ++y;
     336             :         }
     337           0 :         i = y;
     338           0 :         start = aExpr[i++];
     339           0 :         if (start == '\\') {
     340           0 :           start = aExpr[i++];
     341             :         }
     342           0 :         if (::alphanumeric(start) && aExpr[i++] == '-') {
     343           0 :           end = aExpr[i++];
     344           0 :           if (end == '\\') {
     345           0 :             end = aExpr[i++];
     346             :           }
     347             :         }
     348           0 :         if (::alphanumeric(end) && aExpr[i] == ']') {
     349             :           /* This is a range form: a-b */
     350           0 :           T val = aStr[x];
     351           0 :           if (end < start) { /* swap them */
     352           0 :             T tmp = end;
     353           0 :             end = start;
     354           0 :             start = tmp;
     355             :           }
     356           0 :           if (aCaseInsensitive && ::alpha(val)) {
     357           0 :             val = ::_is_char_in_range((unsigned char)start,
     358             :                                       (unsigned char)end,
     359             :                                       (unsigned char)val);
     360           0 :             if (neg == val) {
     361           0 :               return NOMATCH;
     362             :             }
     363           0 :           } else if (neg != (val < start || val > end)) {
     364           0 :             return NOMATCH;
     365             :           }
     366           0 :           y = i;
     367             :         } else {
     368             :           /* Not range form */
     369           0 :           int matched = 0;
     370           0 :           for (; aExpr[y] != ']'; ++y) {
     371           0 :             if (aExpr[y] == '\\') {
     372           0 :               ++y;
     373             :             }
     374           0 :             if (aCaseInsensitive) {
     375           0 :               matched |= (::upper(aStr[x]) == ::upper(aExpr[y]));
     376             :             } else {
     377           0 :               matched |= (aStr[x] == aExpr[y]);
     378             :             }
     379             :           }
     380           0 :           if (neg == matched) {
     381           0 :             return NOMATCH;
     382             :           }
     383             :         }
     384             :       }
     385           0 :       break;
     386             :       case '(':
     387           0 :         if (!aExpr[y + 1]) {
     388           0 :           return ABORTED;
     389             :         }
     390           0 :         return ::_handle_union(&aStr[x], &aExpr[y], aCaseInsensitive,
     391           0 :                                aLevel + 1);
     392             :       case '?':
     393           0 :         break;
     394             :       case ')':
     395             :       case ']':
     396             :       case '|':
     397           0 :         return ABORTED;
     398             :       case '\\':
     399           0 :         ++y;
     400             :         MOZ_FALLTHROUGH;
     401             :       default:
     402           0 :         if (aCaseInsensitive) {
     403           0 :           if (::upper(aStr[x]) != ::upper(aExpr[y])) {
     404           0 :             return NOMATCH;
     405             :           }
     406             :         } else {
     407           0 :           if (aStr[x] != aExpr[y]) {
     408           0 :             return NOMATCH;
     409             :           }
     410             :         }
     411           0 :         break;
     412             :     }
     413             :   }
     414           0 :   return (aStr[x] ? NOMATCH : MATCH);
     415             : }
     416             : 
     417             : template<class T>
     418             : static int
     419           0 : ns_WildCardMatch(const T* aStr, const T* aXp, bool aCaseInsensitive)
     420             : {
     421           0 :   T* expr = nullptr;
     422           0 :   int ret = MATCH;
     423             : 
     424           0 :   if (!nsCharTraits<T>::find(aXp, nsCharTraits<T>::length(aXp), T('~'))) {
     425           0 :     return ::_shexp_match(aStr, aXp, aCaseInsensitive, 0);
     426             :   }
     427             : 
     428           0 :   expr = (T*)moz_xmalloc((nsCharTraits<T>::length(aXp) + 1) * sizeof(T));
     429           0 :   if (!expr) {
     430           0 :     return NOMATCH;
     431             :   }
     432           0 :   memcpy(expr, aXp, (nsCharTraits<T>::length(aXp) + 1) * sizeof(T));
     433             : 
     434           0 :   int x = ::_scan_and_copy(expr, T('~'), T('\0'), static_cast<T*>(nullptr));
     435           0 :   if (x != ABORTED && expr[x] == '~') {
     436           0 :     expr[x++] = '\0';
     437           0 :     ret = ::_shexp_match(aStr, &expr[x], aCaseInsensitive, 0);
     438           0 :     switch (ret) {
     439             :       case NOMATCH:
     440           0 :         ret = MATCH;
     441           0 :         break;
     442             :       case MATCH:
     443           0 :         ret = NOMATCH;
     444           0 :         break;
     445             :       default:
     446           0 :         break;
     447             :     }
     448             :   }
     449           0 :   if (ret == MATCH) {
     450           0 :     ret = ::_shexp_match(aStr, expr, aCaseInsensitive, 0);
     451             :   }
     452             : 
     453           0 :   free(expr);
     454           0 :   return ret;
     455             : }
     456             : 
     457             : template<class T>
     458             : int
     459           0 : NS_WildCardMatch_(const T* aStr, const T* aExpr, bool aCaseInsensitive)
     460             : {
     461           0 :   int is_valid = NS_WildCardValid(aExpr);
     462           0 :   switch (is_valid) {
     463             :     case INVALID_SXP:
     464           0 :       return -1;
     465             :     default:
     466           0 :       return ::ns_WildCardMatch(aStr, aExpr, aCaseInsensitive);
     467             :   }
     468             : }
     469             : 
     470             : int
     471           0 : NS_WildCardMatch(const char* aStr, const char* aXp, bool aCaseInsensitive)
     472             : {
     473           0 :   return NS_WildCardMatch_(aStr, aXp, aCaseInsensitive);
     474             : }
     475             : 
     476             : int
     477           0 : NS_WildCardMatch(const char16_t* aStr, const char16_t* aXp,
     478             :                  bool aCaseInsensitive)
     479             : {
     480           0 :   return NS_WildCardMatch_(aStr, aXp, aCaseInsensitive);
     481           9 : }

Generated by: LCOV version 1.13