/**********************************************************************
 *
 *  Projet  : DilibUnix
 *  Module  : RegExp
 *  Fichier : RegExp.c
 *  Auteur  : F. Parmentier
 *  $Id: RegExp.c,v 1.7 2005/07/08 13:04:50 parmentf Exp $
 *
 **********************************************************************
 * 
 *     Copyright (C) 2004 INIST-CNRS
 *
 **********************************************************************/
/**
   @file

   @brief Functions for RegExp handling

   These functions handle ADVANCED regular expressions.
   
   @author &copy; 1994 INIST-CNRS
   @author Jacques DUCLOY <DilibMaster@inist.fr>
   @author Fran&ccedil;ois PARMENTIER <DilibMaster@inist.fr>

   @see regcomp(3), regex(5)
 */

#include <stdlib.h>    /* malloc, free */
#include <stdio.h>     /* perror */
#include <string.h>    /* strdup */
#include "RegExp.h"
#include "Buffer.h"

/** Points on the beginning of a matching string after @ref RegExpExec. */
char *RegExpLoc1 = NULL;
/** Points on the beginning of the string following the matching
    sub-string after @ref RegExpExec and @ref RegExpExtract. */
char *RegExpLoc2 = NULL;

/**
   Creates and initializes a RegExp.

   @param strInput a string containing a regular expression
   @return the created RegExp or NULL, if it did not succeed.
 */
RegExp *RegExpCreate(char *strInput)
{
  int     regError;
  RegExp *newRegExp = (RegExp *)malloc(sizeof(RegExp));
  newRegExp->preg   = (regex_t *)malloc(sizeof(regex_t));
  regError = regcomp(newRegExp->preg, strInput, REG_EXTENDED);
  if(regError) {
    size_t sizeBuf = regerror(regError,newRegExp->preg,(char *)NULL,(size_t)0);
    char *errBuf   = (char *)malloc(sizeBuf);
    regerror(regError, newRegExp->preg, errBuf, 500);
    perror(errBuf);
    free(errBuf);
    RegExpFree(newRegExp);
    newRegExp = NULL;
  }
  return newRegExp;
} /* RegExpCreate() */

/**
   Frees @a re, and all its dynamically allocated resources.
   
   @param re the RegExp to free
 */
void RegExpFree(RegExp *re)
{
  if(re) {
    if(re->preg) {
      regfree(re->preg); /* Free all internal structures */
      free(re->preg);
      re->preg = NULL;
    }
    free(re);
    re = NULL;
  }
  else {
    perror("No regular expression given to free!");
  }
} /* RegExpFree() */

/**
   Returns a pointer on the beginning of the matched substring.
   
   @param regExp the regular expression to find
   @param strInput the string to search
   @returns a pointer on the beginning of the matched substring.
 */
char *RegExpFind(RegExp *regExp, char *strInput)
{
  regmatch_t pmatch[1];
  char      *strSub = NULL;
  int error = regexec(regExp->preg, strInput, (size_t) 1, pmatch, 0);
  if(!error) {
    strSub = strInput+pmatch[0].rm_so;
  }

  return strSub;
} /* RegExpFind() */

/**
   Returns a copy of the matched substring.
   
   After the call, the variable @ref RegExpLoc2 will contain a pointer
   on the character following the matched string.

   @param regExp the regular expression to match
   @param strInput the string in which to find @a regExp

   @return a copy of the substring matching @a regExp in @a strInput

   @warning Don't forget to free the return string.
 */
char *RegExpExtract(RegExp *regExp, char *strInput)
{
  int l1;
  int n1;
  regmatch_t pmatch[1];
  char *strSubDup = NULL;
  int error = regexec(regExp->preg, strInput, (size_t) 1, pmatch, 0);
  if(!error) {
    l1= pmatch[0].rm_eo-pmatch[0].rm_so;
    n1=pmatch[0].rm_so;
    strSubDup = strdup(BufferGetSubString(strInput,n1,l1));
    RegExpLoc1 = strInput+n1;
    RegExpLoc2 = strInput+pmatch[0].rm_eo; /* Ascending Compatibility */
  }
  
  return strSubDup;
} /* RegExpExtract() */

/**
   Returns a pointer on the character following the string matching @a
   regExp in @a strInput.
   
   @param regExp the regular expression to match
   @param strInput the string in which to find @a regExp

   @return a pointer on the character following the string matching @a
           regExp in @a strInput.
 */
char *RegExpAfter(RegExp *regExp, char *strInput)
{
  regmatch_t pmatch[1];
  char *strAfter = NULL;
  int error = regexec(regExp->preg, strInput, (size_t) 1, pmatch, 0);
  if(!error) {
    strAfter = strInput+pmatch[0].rm_eo;
  }
  return strAfter;
} /* RegExpAfter() */


/**
   Returns a copy of the substring located before the matched
   substring.
   
   @param regExp the regular expression to match
   @param strInput the string in which to find @a regExp

   @return a copy of the substring located before the substring
           matching @a regExp in @a strInput

   @warning Don't forget to free the return string.
 */
char *RegExpBefore(RegExp *regExp, char *strInput)
{
  regmatch_t pmatch[1];
  char *strBefore = NULL;
  int error = regexec(regExp->preg, strInput, (size_t) 1, pmatch, 0);
  if(!error) {
    strBefore = strdup(BufferGetSubString(strInput,0,pmatch[0].rm_so));
  }
  return strBefore;
} /* RegExpBefore() */

/**
   Execute the search of @a regExp in @a strInput.
   Says whether or not @a regExp is in @a strInput.

   If it is in, sets @ref RegExpLoc1 to the beginning of the matching
   sub-string, and @ref RegExpLoc2 to the string following this
   matching sub-string.

   @param regExp Regular expression to execute.
   @param strInput string to search
   @return 1 if @a re was found in @a strInput
   @return 0 if @a re was not found in @a strInput
 */
int RegExpExec(RegExp *regExp, char *strInput)
{
  regmatch_t pmatch[1];
  int error = regexec(regExp->preg, strInput, (size_t) 1, pmatch, 0);
  if(!error) {
    RegExpLoc1 = strInput+pmatch[0].rm_so;
    RegExpLoc2 = strInput+pmatch[0].rm_eo;
    return 1;
  }
  else {
    RegExpLoc1 = NULL;
    RegExpLoc2 = NULL;
    return 0;
  }
} /* RegExpExec() */
