// (c) 1996-2007 Electronic Arts Inc.

/*------------------------------------------------------------------*/
/*                                                                  */
/*                          Regex v1.00                             */
/*                                                                  */
/*                       by Yggy King, EAC                          */
/*                                                                  */
/*                   Main program - May 22, 1996                    */
/*                                                                  */
/*------------------------------------------------------------------*/
/*                                                                  */
/* Version Date   SE History:                                       */
/* ------- ----   -- --------                                       */
/*    1.00 950725 YK initial version                                */
/*                                                                  */
/*------------------------------------------------------------------*/
/*                                                                  */
/*  Notes:                                                          */
/*  - these are convenience functions wrapping Gnu code             */
/*                                                                  */
/*------------------------------------------------------------------*/
/*                                                                  */
/*  TODO:                                                           */
/*  - gimex memory management                                       */
/*  - better docs                                                   */
/*------------------------------------------------------------------*/

/*
    Regular Expressions

A regular expression specifies a set of character strings.
A member of this set of strings is said to be matched by the regular
expression. Regular expressions are constructed as follows:

The following one-character regular expressions match a single character:

1.1 An ordinary character (not one of those discussed in 1.2 below) is
    a one-character regular expression that matches itself.

1.2 A backslash (\) followed by any special character is a one-
    character regular expression that matches the special character
    itself.  The special characters are:

    a. '.', '*', '[', and '\' (period, asterisk, left square bracket, and
      backslash, respectively), which are always special, except
      when they appear within square brackets (see 1.4 below).

    b. '^' (caret or circumflex), which is special at the beginning
      of a regular expression (see 4.1 and 4.3 below), or when it
      immediately follows the left of a pair of square brackets (see 1.4 below).

    c. '$' (dollar sign), which is special at the end of a regular
      expression (see 4.2 below).

1.3 A period (.) is a one-character regular expression that matches
    any character, including supplementary code set characters, except
    new-line.

1.4 A non-empty string of characters enclosed in square brackets
    is a one-character regular expression that matches one character,
    including supplementary code set characters, in that string.  If,
    however, the first character of the string is a circumflex (^),
    the one-character regular expression matches any character,
    including supplementary code set characters, except new-line and
    the remaining characters in the string.  The ^ has this special
    meaning only if it occurs first in the string.  The minus (-) may
    be used to indicate a range of consecutive characters, including
    supplementary code set characters; for example, [0-9] is
    equivalent to [0123456789].

    The '-' loses this special meaning if it occurs first (after an
    initial ^, if any) or last in the string.  The right square
    bracket (]) does not terminate such a string when it is the first
    character within it (after an initial ^, if any); for example,
    []a-f] matches either a right square bracket or one of the
    ASCII letters 'a' through 'f' inclusive.  The four characters listed
    in 1.2.a above stand for themselves within such a string of
    characters.

The following rules may be used to construct regular expressions from
one-character regular expressions:

2.1 A one-character regular expression is an regular expression that
    matches whatever the one-character regular expression matches.

2.2 A one-character regular expression followed by an asterisk (*) is
    a regular expression that matches zero or more occurrences of the
    one-character regular expression, which may be a supplementary
    code set character.  If there is any choice, the longest leftmost
    string that permits a match is chosen.

2.3 The concatenation of regular expressions is an regular expression
    that matches the concatenation of the strings matched by each
    component of the regular expression.

2.4 A regular expression enclosed between the character sequences '\('
    and '\)' is an regular expression that matches whatever the
    unadorned regular expression matches.

A regular expression may be constrained to match words.

3.1  '\<' constrains a regular expression to match the beginning of a
    string or to follow a character that is not a digit, underscore,
    or letter.  The first character matching the regular expression
    must be a digit, underscore, or letter.

3.2 '\>' constrains a regular expression to match the end of a string or
    to precede a character that is not a digit, underscore, or letter.

A regular expression may be constrained to match only an initial segment
or final segment of a line (or both).

4.1 A circumflex (^) at the beginning of a regular expression
    constrains that regular expression to match an initial segment of
    a line.

4.2 A dollar sign ($) at the end of an entire regular expression
    constrains that regular expression to match a final segment of a
    line.

4.3 The construction ^regexp$ constrains the regular
    expression to match the entire line.

*/


    /* Use malloc for memory management. */
#if 1
#include <malloc.h>
#define REGEX_MALLOC
#else
#define alloca  galloc
#define free    gfree
#endif

    /* OK to include string.h. */
#define HAVE_STRING_H   1

    /* Max number of substring matches. */
#define MAX_SUBSTRINGS  32

    /* Syntax rules to enforece; see gnuregex.h for details. */
#define RE_SYNTAX   RE_BACKSLASH_ESCAPE_IN_LISTS    |    \
/*                  RE_BK_PLUS_QM                   | */ \
                    RE_CHAR_CLASSES                 |    \
/*                  RE_CONTEXT_INDEP_ANCHORS        | */ \
/*                  RE_CONTEXT_INDEP_OPS            | */ \
/*                  RE_CONTEXT_INVALID_OPS          | */ \
/*                  RE_DOT_NEWLINE                  | */ \
                    RE_DOT_NOT_NULL                 |    \
                    RE_HAT_LISTS_NOT_NEWLINE        |    \
                    RE_INTERVALS                    |    \
/*                  RE_LIMITED_OPS                  | */ \
/*                  RE_NEWLINE_ALT                  | */ \
                    RE_NO_BK_BRACES                 |    \
                    RE_NO_BK_PARENS                 |    \
/*                  RE_NO_BK_REFS                   | */ \
                    RE_NO_BK_VBAR                   |    \
/*                  RE_NO_EMPTY_RANGES              | */ \
                    RE_UNMATCHED_RIGHT_PAREN_ORD

    /* Include Gnu regular expression matching code. */
#include "gnuregex.c"

    /* Regex API definition. */
#include "regex.h"

/*-----------------------------------------------------------
    Global variables
  -----------------------------------------------------------*/

static struct re_pattern_buffer    _rebuf;
static struct re_registers         _reregs;

    /* Beginning and end of RE matches. 0th entry is
       entire match, 1-N are substring matches. Substrings
       may be nested, but N-th entry always refers to the
       substring matched by the N-th open parenthesis. */
static char *re_start[MAX_SUBSTRINGS] = {NULL, };
static char *re_end[MAX_SUBSTRINGS] = {NULL, };
static int  nsubstrings = 0;

/*-----------------------------------------------------------
    Local functions
  -----------------------------------------------------------*/

static int re_prepare(char *pattern, char *string)
{
        /* Initialize buffer. */
    _rebuf.allocated = 0;
    _rebuf.buffer = 0;
    _rebuf.fastmap = 0;
    _rebuf.translate = 0;
    _rebuf.no_sub = 0;      /* Allow substrings. */
    _rebuf.newline_anchor = 1;
    _rebuf.regs_allocated = REGS_UNALLOCATED;

        /* Compile pattern. */
    re_set_syntax(RE_SYNTAX);
    if (re_compile_pattern(pattern, strlen(pattern), &_rebuf) != 0)
        return 0;
    return 1;
}

static void re_finish(char *pattern, char *string)
{
    int i;

        /* Find start and end of each substring. */
    for (i = 0; (i < _reregs.num_regs) && (_reregs.start[i] >= 0); ++i)
    {
        re_start[i] = &string[_reregs.start[i]];
        re_end[i] = &string[_reregs.end[i]];
    }

        /* Set number of substrings. */
    nsubstrings = i;

        /* Set remaining matches to NULL. */
    while (i < MAX_SUBSTRINGS)
    {
        re_start[i] = NULL;
        re_end[i] = NULL;
        ++i;
    }

        /* Clean up. */
    regfree(&_rebuf);
    if (_reregs.num_regs > 0)
    {
        free(_reregs.start);
        free(_reregs.end);
    }
}

/*-----------------------------------------------------------
    Exported functions
  -----------------------------------------------------------*/

/*
    REmatch - match a regular expression to a string

    Returns string if matched, NULL if not matched. The
    match is not constrained to match the whole string;
    use REstart/REend/RElength to find what actually
    matched.
 */

char    *REmatch(char *pattern, char *string)
{
    re_prepare(pattern, string);
    re_match(&_rebuf, string, strlen(string), 0, &_reregs);
    re_finish(pattern, string);

    return re_start[0];
}

/*
    REsearch - find a regular expression to a string

    Returns pointer to substring if matched, NULL if
    not matched. Use REstart/REend/RElength to find
    what actually matched.
 */

char    *REsearch(char *pattern, char *string)
{
    re_prepare(pattern, string);
    re_search(&_rebuf, string, strlen(string), 0, strlen(string), &_reregs);
    re_finish(pattern, string);

    return re_start[0];
}

/*
    REstart - return a pointer to start of regular expression match

    After calling REmatch or REsearch, REstart returns a pointer
    to the [sub]string that matched the regular expression. Argument
    n is the substring index.
 */

char    *REstart(int n)
{
    if (n < MAX_SUBSTRINGS)
        return re_start[n];
    else
        return NULL;
}

/*
    REend - return a pointer to end of regular expression match

    After calling REmatch or REsearch, REend returns a pointer
    to one byte past the [sub]string that matched the regular
    expression. Argument n is the substring index.
 */

char    *REend(int n)
{
    if (n < MAX_SUBSTRINGS)
        return re_end[n];
    else
        return NULL;
}

/*
    RElength - return length of regular expression match

    After calling REmatch or REsearch, RElength returns the length
    of the [sub]string that matched the regular expression.
    Argument n is the substring index.
 */

int     RElength(int n)
{
    if (n < MAX_SUBSTRINGS)
        return (re_end[n] - re_start[n]);
    else
        return 0;
}

/*
    REnmatches - return number of substring matches

    After calling REmatch or REsearch, REnmatches returns the
    number of substring matches that occurred. Substring matches
    are bracketed by parentheses. The 0-th substring is
    always the entire match, with subsequent substring matches
    being nested within outer substrings.
*/

int     REnmatches(void)
{
    return nsubstrings;
}

#if 0

/*-----------------------------------------------------------
    Test code
  -----------------------------------------------------------*/

void showmatch()
{
    int  matches, i;

    matches = REnmatches();
    printf("%d matches:\n", matches);

    for (i = 0; i < matches; ++i)
    {
        printf("  %2d: [%s][%s][%d]\n", i, REstart(i), REend(i), RElength(i));
    }
}

int main(int argc, char *argv[])
{
    if (argc < 3)
    {
        printf("Usage\n\tregex pattern string\n");
        return 1;
    }

    printf("MATCH:\n");
    REmatch(argv[1], argv[2]);
    showmatch();

    printf("FIND:\n");
    REsearch(argv[1], argv[2]);
    showmatch();

    return 0;
}

#endif
