Commit da5f4b1d authored by Patrick Monnerat's avatar Patrick Monnerat
Browse files

fnmatch: pattern syntax can no longer fail

Whenever an expected pattern syntax rule cannot be matched, the
character starting the rule loses its special meaning and the parsing
is resumed:
- backslash at the end of pattern string matches itself.
- Error in [:keyword:] results in set containing :\[dekorwy.

Unit test 1307 updated for this new situation.

Closes #2273
parent fcaa1826
Loading
Loading
Loading
Loading
+80 −113
Original line number Diff line number Diff line
@@ -46,11 +46,6 @@
#define CURLFNM_SPACE   (CURLFNM_CHARSET_LEN + 9)
#define CURLFNM_UPPER   (CURLFNM_CHARSET_LEN + 10)

typedef enum {
  CURLFNM_LOOP_DEFAULT = 0,
  CURLFNM_LOOP_BACKSLASH
} loop_state;

typedef enum {
  CURLFNM_SCHS_DEFAULT = 0,
  CURLFNM_SCHS_RIGHTBR,
@@ -86,12 +81,12 @@ static int parsekeyword(unsigned char **pattern, unsigned char *charset)
      return SETCHARSET_FAIL;
    switch(state) {
    case CURLFNM_PKW_INIT:
      if(ISALPHA(c) && ISLOWER(c))
      if(ISLOWER(c))
        keyword[i] = c;
      else if(c == ':')
        state = CURLFNM_PKW_DDOT;
      else
        return 0;
        return SETCHARSET_FAIL;
      break;
    case CURLFNM_PKW_DDOT:
      if(c == ']')
@@ -186,15 +181,10 @@ static int setcharset(unsigned char **p, unsigned char *charset)
        (*p)++;
      }
      else if(c == '[') {
        char c2 = *((*p) + 1);
        if(c2 == ':') { /* there has to be a keyword */
          (*p) += 2;
          if(parsekeyword(p, charset)) {
            state = CURLFNM_SCHS_DEFAULT;
          }
          else
            return SETCHARSET_FAIL;
        }
        unsigned char *pp = *p + 1;

        if(*pp++ == ':' && parsekeyword(&pp, charset))
          *p = pp;
        else {
          charset[c] = 1;
          (*p)++;
@@ -248,14 +238,11 @@ static int setcharset(unsigned char **p, unsigned char *charset)
        goto fail;
      break;
    case CURLFNM_SCHS_RIGHTBRLEFTBR:
      if(c == ']') {
      if(c == ']')
        return SETCHARSET_OK;
      }
      else {
      state  = CURLFNM_SCHS_DEFAULT;
      charset[c] = 1;
      (*p)++;
      }
      break;
    }
  }
@@ -266,52 +253,46 @@ fail:
static int loop(const unsigned char *pattern, const unsigned char *string,
                int maxstars)
{
  loop_state state = CURLFNM_LOOP_DEFAULT;
  unsigned char *p = (unsigned char *)pattern;
  unsigned char *s = (unsigned char *)string;
  unsigned char charset[CURLFNM_CHSET_SIZE] = { 0 };
  int rc = 0;

  for(;;) {
    switch(state) {
    case CURLFNM_LOOP_DEFAULT:
      if(*p == '*') {
    unsigned char *pp;

    switch(*p) {
    case '*':
      if(!maxstars)
        return CURL_FNMATCH_NOMATCH;
        while(*(p + 1) == '*') /* eliminate multiple stars */
      while(p[1] == '*') /* eliminate multiple stars */
        p++;
        if(*s == '\0' && *(p + 1) == '\0')
      if(*s == '\0' && p[1] == '\0')
        return CURL_FNMATCH_MATCH;
      rc = loop(p + 1, s, maxstars - 1); /* *.txt matches .txt <=>
                                            .txt matches .txt */
      if(rc == CURL_FNMATCH_MATCH)
        return CURL_FNMATCH_MATCH;
        if(*s) /* let the star eat up one character */
          s++;
        else
      if(!*s)
        return CURL_FNMATCH_NOMATCH;
      s++; /* let the star eat up one character */
      break;
    case '?':
      if(!*s)
        return CURL_FNMATCH_NOMATCH;
      }
      else if(*p == '?') {
        if(ISPRINT(*s)) {
      s++;
      p++;
        }
        else if(*s == '\0')
          return CURL_FNMATCH_NOMATCH;
        else
          return CURL_FNMATCH_FAIL; /* cannot deal with other character */
      }
      else if(*p == '\0') {
        if(*s == '\0')
          return CURL_FNMATCH_MATCH;
        return CURL_FNMATCH_NOMATCH;
      }
      else if(*p == '\\') {
        state = CURLFNM_LOOP_BACKSLASH;
      break;
    case '\0':
      return *s? CURL_FNMATCH_NOMATCH: CURL_FNMATCH_MATCH;
    case '\\':
      if(p[1])
        p++;
      }
      else if(*p == '[') {
        unsigned char *pp = p + 1; /* cannot handle with pointer to register */
      if(*s++ != *p++)
        return CURL_FNMATCH_NOMATCH;
      break;
    case '[':
      pp = p + 1; /* Copy in case of syntax error in set. */
      if(setcharset(&pp, charset)) {
        int found = FALSE;
        if(!*s)
@@ -342,32 +323,18 @@ static int loop(const unsigned char *pattern, const unsigned char *string,
        if(charset[CURLFNM_NEGATE])
          found = !found;

          if(found) {
        if(!found)
          return CURL_FNMATCH_NOMATCH;
        p = pp + 1;
        s++;
        break;
      }
          else
            return CURL_FNMATCH_NOMATCH;
        }
        else {
          if(*p++ != *s++)
            return CURL_FNMATCH_NOMATCH;
        }
      }
      else {

      /* Syntax error in set: this must be taken as a regular character. */
      /* FALLTHROUGH */
    default:
      if(*p++ != *s++)
        return CURL_FNMATCH_NOMATCH;
      }
      break;
    case CURLFNM_LOOP_BACKSLASH:
      if(ISPRINT(*p)) {
        if(*p++ == *s++)
          state = CURLFNM_LOOP_DEFAULT;
        else
          return CURL_FNMATCH_NOMATCH;
      }
      else
        return CURL_FNMATCH_FAIL;
      break;
    }
  }
+3 −2
Original line number Diff line number Diff line
@@ -25,7 +25,6 @@

#define MATCH   CURL_FNMATCH_MATCH
#define NOMATCH CURL_FNMATCH_NOMATCH
#define RE_ERR  CURL_FNMATCH_FAIL

struct testcase {
  const char *pattern;
@@ -135,6 +134,8 @@ static const struct testcase tests[] = {
  { "[^[:blank:]]",             "\t",                     NOMATCH },
  { "[^[:print:]]",             "\10",                    MATCH },
  { "[[:lower:]][[:lower:]]",   "ll",                     MATCH },
  { "[[:foo:]]",                "bar",                    NOMATCH },
  { "[[:foo:]]",                "f]",                     MATCH },

  { "Curl[[:blank:]];-)",       "Curl ;-)",               MATCH },
  { "*[[:blank:]]*",            " ",                      MATCH },
@@ -172,7 +173,7 @@ static const struct testcase tests[] = {
  { "x",                        "",                       NOMATCH },

  /* backslash */
  { "\\",                       "\\",                     RE_ERR },
  { "\\",                       "\\",                     MATCH },
  { "\\\\",                     "\\",                     MATCH },
  { "\\\\",                     "\\\\",                   NOMATCH },
  { "\\?",                      "?",                      MATCH },