/*	parse_utils - precision-independent pieces of parse		*/

#define PARSE_UTILS_VERSION "parse_utils v 1.0 11 Feb 2009"

#define LIKE_MATCH_ANY '%'
#define LIKE_MATCH_1 '_'
#define LIKE_ESC '\\'

  /*  See core.h for fancier defn.  defining here so we do NOT have to	*/
  /*  include core.h, which requires OPTIONS, etc			*/
#define FALSE 0
#define TRUE 1

#include <stdlib.h>

char *parse_utils_return_vers()
/*  Dummy routine.  Exists only to force file version string into	*/
/*  this module.  Note string must not be global or we'll have con-	*/
/*  flicts if another routine similarly includes the version string	*/
{
  static char version[] = PARSE_UTILS_VERSION;
  return version;
}

char *analyze_wilds(s)
char *s;
  /*  Given a string that starts with a "like" wildcard character	*/
  /*  Remove all but one any-chars wild and put that after		*/
  /*    any single-char wilds.  (Essentially, a string of wildcards	*/
  /*    becomes "match N or more chars", where N is the number of	*/
  /*    single-char wilds).						*/
  /*  Return a pointer to the next non-wild char			*/
{
  char *strdupl();	/* from utils.c					*/

  char *exp,*in_ptr,*out_ptr;
  char in_match_any_state,in_escape_state;

  in_match_any_state = FALSE;
  in_escape_state = FALSE;
  out_ptr = in_ptr = strdupl(&exp,s,"duplicating like expression");

  while (*in_ptr != '\0') {
    if (in_escape_state) {
	/*  This section copies the character after the \, including,	*/
	/*  particular, an escaped \ or match char.  If the string 	*/
	/*  improperly ended with \, it won't be detected here, but	*/
	/*  hopefully WILL be detected in strlike			*/
      *out_ptr++ = *in_ptr++;      
      in_escape_state = FALSE;
    } else if (*in_ptr == LIKE_MATCH_ANY) {
      in_ptr++;
      in_match_any_state = TRUE;
    } else if (*in_ptr == LIKE_MATCH_1) {
      *out_ptr++ = *in_ptr++;      
    } else {
	/*  This section copies an escaping \ (along w/non-match chars)	*/
      in_escape_state = (*in_ptr == LIKE_ESC);
      if (in_match_any_state) {
	*out_ptr++ = LIKE_MATCH_ANY;
	in_match_any_state = FALSE;
      }
      *out_ptr++ = *in_ptr++;      
    }
  }

  if (in_match_any_state) *out_ptr++ = LIKE_MATCH_ANY;
  *out_ptr = '\0';

  return exp;
}

int strlike(s,like_exp,err)
char *s,*like_exp;
void (*err)();
  /*  Implementation of SQL "like".					*/
  /*    s is the candidate string.  like_exp is a "like expression"	*/
  /*    consisting of normal characters and wildcard characters.	*/
  /*    wildcard characters are of 2 kinds; _ which represents		*/
  /*    exactly 1 character, and @ which represents 0 or more 		*/
  /*    characters.  (The actual characters used are compiled into	*/
  /*    this program.  @ and _ are what were documented in the		*/
  /*    Oracle description).  Characters can be escaped with a		*/
  /*    preceding \ (or other char compiled into program)		*/
  /*  Returns 0 if match; positive integer indicating point of		*/
  /*    non-match							*/
{
  char *s_ptr;
  char *exp,*exp_ptr;
  int ret_val;
  char prev_char_matched;

  ret_val = 0;
  prev_char_matched = TRUE;

  s_ptr = s;
  exp_ptr = exp = analyze_wilds(like_exp);

  while ((*exp_ptr != '\0') && (*s_ptr != '\0') && prev_char_matched) {
    switch (*exp_ptr) {
      case LIKE_MATCH_1:
	exp_ptr++;
	s_ptr++;
	break;
      case LIKE_MATCH_ANY:
	  /*  When s_ptr matches char after wildcard, reset to do	*/
	  /*  test again through default: case, putting us back on	*/
	  /*  "normal" track.						*/
	if (*s_ptr++ == *(exp_ptr+1)) {
	  s_ptr--;
	  exp_ptr++;
	}
	break;
      case LIKE_ESC:
	if (*(++exp_ptr) == '\0') 
	  err ("like expression may not end w/escape.  expression: ",exp);
				/*  Deliberately fall through		*/
      default:
	prev_char_matched = (*s_ptr++ == *exp_ptr++);
	break;
    }
  }
    /*  Take care of case where everything matched up to a final	*/
    /*  match-any char							*/
  if (*exp_ptr == LIKE_MATCH_ANY) exp_ptr++;

  ret_val = ((*exp_ptr == '\0') && (*s_ptr == '\0') && prev_char_matched) ? 
								0 : s_ptr - s;
  free (exp);
  return ret_val;
}
