/*******
 Made reentrant by Paul Kienitz 12/31/90.  Used function prototypes just to
 avoid booboos.  Also replaced _toupper with real toupper.  I hope someday to
 eliminate the error return of CmplPat; make missing parens be assumed to be
 at the beginning or end as appropriate, and an empty half of | be treated
 like a %.  I guess ' at the end is ignored?  Actually it already ignores
 extra right parens at the end.  But with an extra right paren in the middle
 it ignores everything after it.
*******/

/* PatMatch.c - Implements AmigaDos Regular Expression Pattern Matching.
**
**  This program will test whether a string is an AmigaDos  regular expression
**  It may be used to implement wild expressions such as:
**
**    "copy #?.c to <dir>" to copy any file ending in .c
**
**  The program has two entry points: CmplPat, and Match.
**
**    CmplPat - takes a pattern and returns an auxilliary array of short
**              ints which is used by Match.  The pattern is not modified
**              in any way.  CmplPat returns 1 if no errors were detected
**              while compiling the pattern; otherwise it returns 0;
**
**    Match   - takes the pattern, the auxilliary vector, and the string
**              to be matched.  It returns 1 if the string matches the
**              pattern; otherwise it returns 0;
**
**  Translated from BCPL by:
**              Jeff Lydiatt
**              Richmond B.C. Canada
**              16 May 1986.
**
**  Source: "A Compact Function for Regular Expression Pattern Matching",
**           Software - Practice and Experience, September 1979.
**
**  Useage:
**     To test if "file.c" matches the regular expression "#?.c"
**     char *Pat = "#?.c";
**     char *Str = "file.c";
**     WORD Aux[128];
**
**     if ( CmplPat( Pat, Aux ) == 0 )
**        {
**           printf("Bad Wildcard Expression\n");
**           exit(1);
**        }
**     if ( Match( Pat, Aux, Str ) == 1 )
**        printf("String matches the pattern\n");
**     else
**        printf("String does NOT match the pattern\n");
**/

/*--- Included files ----*/

#include <stdio.h>
#include <exec/types.h>
#include <ctype.h>

#define  EOS '\0'

/*----------------------------------------------------------------*/
/*                   The Interpreter                              */
/*----------------------------------------------------------------*/


struct GM {
    BOOL  Succflag;
    short Wp;
    short *Work;
};


static void Put(short N, register struct GM *m)
{
   register short *ip;
   register short *to;

   if ( N == 0 )
      m->Succflag = TRUE;
   else
      {
	for ( ip = m->Work + 1, to = m->Work + m->Wp; ip <= to; ip++)
	   if ( *ip == N )
	      return;
	m->Work[ ++m->Wp ] = N;
      }
}

long Match( char Pat[], short Aux[], char Str[] )
{
   struct GM m;
   short     W[ 128 ];
   short     S = 0;
   short     I, N, Q, P, Strlength;
   char      K, Ch;
   size_t    strlen(char *s);

   m.Work = W;
   m.Wp = 0;
   m.Succflag = FALSE;
   Strlength = strlen( Str );
   Put( 1, &m );

   if ( Aux[ 0 ] != 0 )
      Put( Aux[ 0 ], &m );

   for(;;)
      {
        /* First complete the closure */
        for( N=1; N <= m.Wp; N++ )
          {
	     P = m.Work[ N ];
	     K = Pat[ P-1 ];
	     Q = Aux[ P ];
	     switch( K )
	   	{
		  case '#': Put( P + 1, &m );
		  case '%': Put( Q, &m );
		  default : break;
		  case '(':
		  case '|': Put( P + 1, &m);
			    if ( Q != 0 )
			       Put( Q, &m );
		}
	   }

	if ( S >= Strlength )
	   return m.Succflag;
	if ( m.Wp == 0 )
	   return FALSE;
	Ch = Str[ S++ ];

	/* Now deal with the match items */

	N = m.Wp;
	m.Wp = 0;
	m.Succflag = FALSE;

	for ( I = 1; I <= N; I++)
	  {
	     P = m.Work[ I ];
	     K = Pat[ P - 1 ];
	     switch( K )
	       {
		 case '#':
		 case '|':
		 case '%':
		 case '(': break;
		 case '\'': K = Pat[ P ];
		 default : if ( toupper( Ch ) != toupper( K ) )
			      break;
		 case '?': /* Successful match */
		 	   Put( Aux[ P ], &m );
		} /* End Switch */
	  } /* End For I */
     } /* End for(;;) */
}


/*----------------------------------------------------------------*/
/*                     The Compiler                               */
/*----------------------------------------------------------------*/

struct CM {
    short *Aux;
    char  *Pat;
    short PatP, Patlen;
    BOOL  Errflag;
    char  Ch;
};

#define CC register struct CM *c



static void Rch( CC ) /* Read next character from Pat */
{
   if ( c->PatP >= c->Patlen )
      c->Ch = EOS;
   else
      c->Ch = c->Pat[ c->PatP++ ];
}



static void Nextitem( CC ) /* Get next char from Pat; recognize ' escape char */
{
   if ( c->Ch == '\'' )
      Rch( c );
   Rch( c );
}


static void Setexits( short List, short Val, short *Aux )
{
   short A;

   do
     {
	A = Aux[ List ];
	Aux[ List ] = Val;
	List = A;
     }
	while ( List != 0 );
}

static short Join( short A, short B, short *Aux )
{
    short T = A;

    if ( A == 0 )
	return B;
    while ( Aux[ A ] != 0 )
	A = Aux[ A ];
    Aux[ A ] = B;
    return T;
}

static short Prim( CC )      /* Parse a Prim symbol */
{
   short   A = c->PatP;
   char Op = c->Ch;
   short  Exp( short A, CC );

   Nextitem( c );
   switch( Op )
     {
        case EOS:				/* empty string after | ? */
        case ')':				/* missing ( ? */
        case '|': c->Errflag = TRUE;		/* empty string before | ? */
        default : return A;
        case '#': Setexits( Prim( c ), A, c->Aux );
		  return A;
        case '(': A = Exp( A, c );
		  if ( c->Ch != ')' )
			c->Errflag = TRUE;	/* missing ) ? */
		  Nextitem( c );
		  return A;
     }
}

static short Exp( short AltP, CC )    /* Parse an expression */
{
   short Exits = 0;
   short A;

   for (;;)
	{
	   A = Prim( c );
	   if ( c->Ch == '|' || c->Ch == ')' || c->Ch == EOS )
	      {
		Exits = Join( Exits, A, c->Aux );
		if ( c->Ch != '|' )
		   return Exits;
		c->Aux[ AltP ] = c->PatP;
		AltP = c->PatP;
		Nextitem( c );
	      }
	   else
	      Setexits( A, c->PatP, c->Aux );
	}
}

long CmplPat( char Pattern[], short CmplPattern[] )
{
   short     i;
   struct CM c;
   size_t    strlen();

   c.Pat = Pattern;
   c.Aux = CmplPattern;
   c.PatP = 0;
   c.Patlen = strlen( c.Pat );
   c.Errflag = FALSE;

   for ( i = 0; i <= c.Patlen; i++ )
      c.Aux[ i ] = 0;
   Rch( &c );
   Setexits( Exp( 0, &c ), 0, &c.Aux[0] );
   return (!c.Errflag);
}
