//
// fnmatch.c
//
// Filename matching
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// This code is derived from software contributed to Berkeley by
// Guido van Rossum.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//

#include <fnmatch.h>
#include <string.h>

#define  EOS '\0'

static const char *rangematch(const char *pattern, int test, int flags);

//
// fnmatch()
//  Compares filename/pathname to a pattern
//
// As specified in POSIX 1003.2-1992, section B.6.
//

int fnmatch(const char *pattern, const char *string, int flags) {
  const char *stringstart = string;
  char c;
  char test;

  for (;;) {
    switch (c = *pattern++) {
      case EOS:
        return *string == EOS ? 0 : FNM_NOMATCH;

      case '?':
        if (*string == EOS) return FNM_NOMATCH;
        if (*string == '/' && (flags & FNM_PATHNAME)) return FNM_NOMATCH;

        if (*string == '.' &&
            (flags & FNM_PERIOD) &&
            (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) {
          return FNM_NOMATCH;
        }

        string++;
        break;

      case '*':
        c = *pattern;
        // Collapse multiple stars
        while (c == '*') c = *++pattern;

        if (*string == '.' &&
            (flags & FNM_PERIOD) &&
            (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) {
          return FNM_NOMATCH;
        }

        // Optimize for pattern with * at end or before
        if (c == EOS) {
          if (flags & FNM_PATHNAME) {
            return strchr(string, '/') == NULL ? 0 : FNM_NOMATCH;
          } else {
            return 0;
          }
        } else if (c == '/' && flags & FNM_PATHNAME) {
          if ((string = strchr(string, '/')) == NULL) return FNM_NOMATCH;
          break;
        }

        // General case, use recursion
        while ((test = *string) != EOS) {
          if (!fnmatch(pattern, string, flags & ~FNM_PERIOD)) return 0;
          if (test == '/' && flags & FNM_PATHNAME) break;
          string++;
        }
        return FNM_NOMATCH;

      case '[':
        if (*string == EOS) return FNM_NOMATCH;
        if (*string == '/' && flags & FNM_PATHNAME) return FNM_NOMATCH;
        if ((pattern = rangematch(pattern, *string, flags)) == NULL) return FNM_NOMATCH;
        string++;
        break;

      case '\\':
        if (!(flags & FNM_NOESCAPE)) {
          if ((c = *pattern++) == EOS) {
            c = '\\';
            pattern--;
          }
        }
        // FALLTHROUGH

      default:
        if (c != *string++) return FNM_NOMATCH;
        break;
    }
  }
}

static const char *rangematch(const char *pattern, int test, int flags) {
  char c, c2;
  int negate, ok;

  //
  // A bracket expression starting with an unquoted circumflex
  // character produces unspecified results (IEEE 1003.2-1992,
  // 3.13.2).  I have chosen to treat it like '!', for
  // consistancy with regular expression syntax.
  //

  if (negate = (*pattern == '!' || *pattern == '^')) pattern++;

  for (ok = 0; (c = *pattern++) != ']';) {
    if (c == '\\' && !(flags & FNM_NOESCAPE)) c = *pattern++;
    if (c == EOS) return NULL;

    if (*pattern == '-' && (c2 = *(pattern + 1)) != EOS && c2 != ']') {
      pattern += 2;
      if (c2 == '\\' && !(flags & FNM_NOESCAPE)) c2 = *pattern++;
      if (c2 == EOS) return NULL;
      if (c <= test && test <= c2) ok = 1;
    } else if (c == test) {
      ok = 1;
    }
  }

  return ok == negate ? NULL : pattern;
}
