#include "config.h"
/*
 * Copyright (c) 1986, 2014 by The Trustees of Columbia University in
 * the City of New York.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  + Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *  + 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.
 *
 *  + Neither the name of Columbia University nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.

 Author: Howie Kaye
*/

#undef TRUE
#define TRUE 1
#undef FALSE
#define FALSE 0
#define NULL 0

#include "args.h"

#ifdef MSDOS
#include <ctype.h>
#define uncase(x) tolower(x)		/* XXX */
#else
#define uncase(x) (x)
#endif

#if HAVE_STDC
#include <stdlib.h>
#include <string.h>
#endif

static const char *get_curly_token ARGS((const char *pattern, char *current));
static const char *nextchr ARGS((const char *pattern, char *lower, char *upper));
static int do_curly ARGS((const char *pattern, const char **string));
static int do_list ARGS((int chr, const char *pattern));
static int match ARGS((const char *pattern, const char *string));

/*
 * pattern matcher.
 * matches patterns with wildcards of:
 * '*' = matches any string
 * '?' = any char
 * '[abc-h]' = any include character or range
 * '{abc,def}' = any included string.  these may include any other wildcard
 */
static int
#if HAVE_STDC
match(const char *pattern, const char *string)
#else /* K&R style */
match(pattern,string)
const char *pattern,*string;
#endif /* HAVE_STDC */
{
  const char *psave,*ssave;		/* back up pointers for failure */
  psave = ssave = NULL;			/* initially no value */
  while (1) {				/* skip first */
    for (; uncase(*pattern) == uncase(*string); pattern++,string++)
      if (*string == '\0') {		/* end of strings, succeed */
	return(1);
      }
    if ((*string != '\0') && (*pattern == '{')) { /* a "{"...try to match it */
      if (do_curly(pattern,&string)) {	/* the curly routine checks the rest */
	return(TRUE);			/* of the string too */
      }
      else {
	if (ssave != NULL && *ssave != '\0') { /* if not at end  */
					/* ...have seen a star */
	  string = ++ssave;		/* skip 1 char from string */
	  pattern = psave;		/* and back up pattern */
	}
	else {
	  return(0);			/* '{' didn't match.   fail. */
	}
      }
    }
    else if ((*string != '\0') && (*pattern == '[')) { /* a '[' */
      if (do_list((int)(*string),pattern)) {	/* try to match the list */
	while (*pattern != ']' && *pattern != '\0') { /* and skip past it */
	  pattern++;
	}
	if (*pattern == ']') pattern++;
	++string;			/* and the string */
      }
      else {
	if (ssave != NULL && *ssave != '\0') { /* if not at end  */
					/* ...have seen a star */
	  string = ++ssave;		/* skip 1 char from string */
	  pattern = psave;		/* and back up pattern */
	}
	else {
	  return(0);			/* no match.  fail */
	}
      }
    } else if (*string != '\0' && *pattern == '?') {
      ++pattern;			/* '?', let it match */
      ++string;
    } else if (*pattern == '*') {	/* '*' ... */
        psave = ++pattern;		/* remember where we saw it */
        ssave = string;			/* let it match 0 chars */
    } else if (ssave != NULL && *ssave != '\0') { /* if not at end  */
					/* ...have seen a star */
      string = ++ssave;			/* skip 1 char from string */
      pattern = psave;			/* and back up pattern */
    } else {
        break;				/* otherwise just fail and return 0 */
    }
  }
  return (0);
}

/*
 * match patterns in square brackets
 */
static int
#if HAVE_STDC
do_list(int chr, const char *pattern)
#else /* K&R style */
do_list(chr,pattern)
int chr;
const char *pattern;
#endif /* HAVE_STDC */
{
  int inverse = 0;			/* test if a "not'ed" pattern */
  int range[128],i;
  char lower, upper;

  for (i = 0; i < 128; i++) range[i] = 0;
  pattern++;				/* pass '[' */
  if (*pattern == '^') {		/* check for negation */
    inverse = TRUE;
    pattern++;				/* and skip past it */
  }

  while(*pattern != ']' && *pattern != '\0') {
    pattern = nextchr(pattern,&lower,&upper); /* fill in chars */
    for(i = uncase(lower); i <= uncase(upper); i++){/* over possible ranges */
      range[i] = 1;
    }
    pattern++;
  }
  return(inverse ^ range[uncase(chr)]);	/* return match based on inverse */
}

/*
 * get next range of chars out of bracket'ed list
 * returns upper and lower bound in lower and upper
 * if not a range, lower == upper
 */

static const char *
#if HAVE_STDC
nextchr(const char *pattern, char *lower, char *upper)
#else /* K&R style */
nextchr(pattern,lower,upper)
const char *pattern;
char *lower,*upper;
#endif /* HAVE_STDC */
{
  if (*pattern == '\\') {		/* a quoted char */
    pattern++;
  }
  *lower = *pattern;			/* get first char */
  *upper = *lower;			/* assume not a range */
  if (pattern[1] == '-') {		/* wrong. */
    pattern+=2;				/* skip the dash. */
    if (*pattern == '\\') {		/* check for quote again */
      pattern++;
    }
    *upper = *pattern;			/* get the upper limit */
  }
  return(pattern);			/* return updated pattern pointer */
}

/*
 * match inside curly braces
 */

static int
#if HAVE_STDC
do_curly(const char *pattern, const char **string)
#else /* K&R style */
do_curly(pattern,string)
const char *pattern,**string;
#endif /* HAVE_STDC */
{
  char current[300];		/* big buffer,  */

  pattern++;
  while(1) {				/* get next token from in braces */
    pattern = get_curly_token(pattern,current);
    if (match(current,*string)) {	/* if a match return success */
      return(TRUE);
    }
    if (*pattern == '\0' || *pattern == '}') /* otherwise, keep */
      break;
    pattern++;				/* going to next token */
  }
  return(FALSE);			/* failure */
}

/*
 * get the next token in curly braces, and append the end of the string
 * to it.
 */
static const char *
#if HAVE_STDC
get_curly_token(const char *pattern, char *current)
#else /* K&R style */
get_curly_token(pattern,current)
const  char *pattern;
char *current;
#endif /* HAVE_STDC */
{
  const char *p = pattern;
  char *cp = current;
  const char *xp;
  int leftcount = 1;
  int commacount = 0;

  while(leftcount > 0) {		/* make sure braces match correctly */
    switch(*p) {
    case '{':				/* if a char is an open brace */
      leftcount++;			/* count it */
      break;
    case '}':				/* likewise a close brace */
      leftcount--;
      break;
    case ',':				/* a "," in the current set of braces*/
      if (leftcount == 1)
	commacount = 1;			/* note end of token */
      break;
    }
    if (!leftcount || commacount || *p == '\0') { /* token ended */
      strncpy(cp,pattern,p-pattern);	/* copy token */
      cp[p-pattern] = '\0';		/* tie it off */
      xp = p;				/* save pointer */
      if (leftcount == 0) xp++;		/* if the close brace, pass it */
      while(leftcount > 0) {		/* then scan to end of pattern */
	if (*xp == '{') leftcount++;
	else if (*xp == '}') leftcount--;
	else if (*xp == '\0') break;
	xp++;
      }
      strcat(cp,xp);			/* append the rest of the string */
      return(p);			/* return pointer to end of token */
    }
    p++;				/* otherwise, keep traversing string */
  }
  return ((char*)NULL);
}

/*
 * pattern match
 * if partial, append a "*" to the pattern
 * if *.* in msdos, then match
 * otherwise, try to match
 */

int
#if HAVE_STDC
fmatch(const char *string, const char *pattern, int partial)
#else /* K&R style */
fmatch(string,pattern,partial)
const char *string,*pattern;
int partial;
#endif /* HAVE_STDC */
{
  int ret;
  char temp[30];
  char *pat = temp;
  if ((int)strlen(pattern)+2 > 29)
      pat = (char *)malloc(strlen(pattern)+2);
  strcpy(pat,pattern);
  if (partial) strcat(pat,"*");
#ifdef MSDOS
  if (!strcmp(pat,"*.*"))
    ret = TRUE;
  else
#endif
  ret = match(pat,string);
  if (pat != temp)
      free(pat);
  return(ret);
}
