/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"@(#)search.c	1.4	92/07/14 SMI"       /* SVr4.0 1.6 */
#include <setjmp.h>
#include <table.h>
#include <internal.h>

extern jmp_buf TLenv;

#define INIT	register char *sp = instring; extern char *TLenvp;
#define GETC()	(*sp++)
#define	PEEKC()	(*sp)
#define	UNGETC(c)	(--sp)
#define	RETURN(c)	return;
#define	ERROR(c)	longjmp( TLenv, 0 )

#include <regexp.h>

#define	ATOI(s,r)	{ \
	unsigned char *ptr;	\
	r = (long)strtol( (char *)s, (char **)&ptr, 0L ); \
	if( ptr == s ) return( FALSE ); \
	}

extern tbl_t TLtables[];
extern unsigned char	TLgenbuf[];
long	strtol();

static int
TLs_strmatch( string, pattern )
unsigned char *string, *pattern;
{
	unsigned char	TLexpbuf[ TL_MAXLINESZ ];

	/* Come back to this point if regexp error */
	if( setjmp( TLenv ) ) return( FALSE );

	(void) compile( (char *)pattern, (char *)TLexpbuf,
		(char *)&TLexpbuf[TL_MAXLINESZ], '\0' );

	return( TLsearch( string, TLexpbuf ) );
}

/*ARGSUSED*/
static int
TLs_op( tid, fieldvalue, sptr )
int tid;
unsigned char *fieldvalue;
TLsearch_t *sptr;
{
	register rc, p_value, f_value, op;

	if (fieldvalue == NULL)
		return (0);

	op = (int)(sptr->ts_operation);
	switch( op ) {
	case TLEQ:
		rc = !Strcmp( fieldvalue, sptr->ts_pattern );
		break;

	case TLNE:
		rc = Strcmp( fieldvalue, sptr->ts_pattern );
		break;

	case TLLT:
		rc = (Strcmp( fieldvalue, sptr->ts_pattern ) < 0);
		break;

	case TLGT:
		rc = (Strcmp( fieldvalue, sptr->ts_pattern ) > 0);
		break;

	case TLLE:
		rc = (Strcmp( fieldvalue, sptr->ts_pattern ) <= 0 );
		break;

	case TLGE:
		rc = (Strcmp( fieldvalue, sptr->ts_pattern ) >= 0 );
		break;

	case TLMATCH:
		rc = TLs_strmatch( fieldvalue, sptr->ts_pattern );
		break;

	case TLNMATCH:
		rc = !TLs_strmatch( fieldvalue, sptr->ts_pattern );
		break;

	case TLNUMEQ:
		ATOI( fieldvalue, f_value )
		ATOI( sptr->ts_pattern, p_value )
		rc = (f_value == p_value);
		break;

	case TLNUMNE:
		ATOI( fieldvalue, f_value )
		ATOI( sptr->ts_pattern, p_value )
		rc = (f_value != p_value);
		break;

	case TLNUMLT:
		ATOI( fieldvalue, f_value )
		ATOI( sptr->ts_pattern, p_value )
		rc = (f_value < p_value);
		break;

	case TLNUMGT:
		ATOI( fieldvalue, f_value )
		ATOI( sptr->ts_pattern, p_value )
		rc = (f_value > p_value);
		break;

	case TLNUMLE:
		ATOI( fieldvalue, f_value )
		ATOI( sptr->ts_pattern, p_value )
		rc = (f_value <= p_value);
		break;

	case TLNUMGE:
		ATOI( fieldvalue, f_value )
		ATOI( sptr->ts_pattern, p_value )
		rc = (f_value >= p_value);
		break;

	default:
		rc = (*(sptr->ts_operation))( sptr->ts_fieldname, fieldvalue,
			sptr->ts_pattern );
		break;
	}
	return( rc );
}

static int
TLs_match( tid, entry, sarray, how_to_match )
int tid, how_to_match;
entry_t *entry;
TLsearch_t *sarray;
{
	register tbl_t	*tptr = TLtables + tid;
	register TLsearch_t *sptr;
	register unsigned char *fptr;
	register accum = (how_to_match == TL_AND || how_to_match == TL_NOR ), rc;
	register offset;

	for( sptr = sarray; sptr->ts_fieldname; sptr++ ) {
		/* Get pointer to string to match against */
		if( !Strcmp( sptr->ts_fieldname, TLCOMMENT ) ) {
			if( !E_NFIELDS(entry) ) fptr = E_COMMENT(entry);
			else return( FALSE );
		} else if( !Strcmp( sptr->ts_fieldname, TLTRAILING ) )
			fptr = E_COMMENT(entry);
		else if( (offset = TLf_find( &(tptr->fieldnames), sptr->ts_fieldname ))
			== -1 )
			return( FALSE );
		else fptr = *E_GETFIELD(entry, offset);
		
		rc = TLs_op( tid, fptr, sptr );
		switch( how_to_match ) {
		case TL_AND:
			accum = accum && rc;
			if( !accum ) return( FALSE );
			break;

		case TL_OR:
			accum = accum || rc;
			if( accum ) return( TRUE );
			break;

		case TL_NAND:
			accum = accum || !rc;
			if( accum ) return( TRUE );
			break;

		case TL_NOR:
			accum = accum && !rc;
			if( !accum ) return( FALSE );
			break;
		}
	}
	return( accum );
}

int
TLs_search( tid, sarray, first, last, how_to_match )
int tid, how_to_match;
TLsearch_t *sarray;
entryno_t first, last;
{
	register tbl_t *tptr = TLtables + tid;
	register TLsearch_t *sptr;
	register entryno_t dot, direction = (first < last), done = FALSE;
	register rc;
	entry_t *entry;
	
	for( sptr = sarray; sptr->ts_fieldname; sptr++ ) {
		if( Strcmp( sptr->ts_fieldname, TLCOMMENT )
			&& Strcmp( sptr->ts_fieldname, TLTRAILING ) 
			&& TLf_find( &(tptr->fieldnames), sptr->ts_fieldname ) == -1 )
			return( TLBADFIELD );
	}

	dot = first;
	while( !done ) {
		/* Get the current entry */
		if( (rc = TLe_getentry( tid, dot, &entry )) != TLOK )
			return( rc );

		if( TLs_match( tid, entry, sarray, how_to_match ) )
			return( dot );
		if( direction ) {
			dot++;
			if( dot > last ) done = TRUE;
		} else {
			dot--;
			if( dot < last ) done = TRUE;
		}
	}
	return( TLFAILED );
}

static unsigned char *
TLplace( sp, l1, l2, limit )
register unsigned char *sp, *l1, *l2, *limit;
{

	while (l1 < l2) {
		*sp++ = *l1++;
		if( sp >= limit ) return( 0 );
	}
	return( (unsigned char *)sp );
}

/*
	Search for pattern in string; replace with replacetxt and leave
	the result in TLgenbuf;
*/
static int
TLsubstitute( string, pattern, replacetxt )
unsigned char *string, *pattern, *replacetxt;
{
	if( !TLs_strmatch( string, pattern ) ) return( 0 );
	return( TLreplace( string, replacetxt ) );
}

static int
TLreplace( string, text )
unsigned char *string, *text;
{
	register unsigned char *lp, *sp, *rp, *limit;
	unsigned char c, buf[2];
	register offset;

	lp = (unsigned char *)string;
	sp = TLgenbuf;
	rp = (unsigned char *)text;
	limit = (unsigned char *)(TLgenbuf + TL_MAXLINESZ);

	/* Copy text from before match (loc1 points at 1st char of matched text) */
	while (lp < (unsigned char *)loc1)
		*sp++ = *lp++;

	/* Copy Matched portion */
	while( c = *rp++ ) {
		if( c == '&' ) {
			if( !(sp = TLplace( sp, (unsigned char *)loc1, (unsigned char *)loc2,
				limit ) ) ) 
				return( 0 ); 
			continue;
		} else if( c == '\\' ) {
			buf[0] = c = *rp++;
			buf[1] = '\0';
			offset = atoi( (char *)buf );
			if( offset > 0 && offset <= nbra ) {
				sp = TLplace( sp, (unsigned char *)braslist[ offset - 1 ],
					(unsigned char *)braelist[ offset - 1 ], limit );
				if( !sp ) return( 0 );
				continue;
			}
		}
		*sp++ = c;
		if( sp >= limit )
			return( 0 );
	}

	/* Copy portion AFTER matched text */
	lp = (unsigned char *)loc2;
	while( *sp++ = *lp++ )
		if( sp >= limit )
			return( 0 );
	return( 1 );
}

static
TLsearch( string, expbuf )
unsigned char *string, *expbuf;
{
	register c;

	/* Reset Braced expression list */
	for( c = 0; c < NBRA; c++ ) {
		braslist[c] = 0;
		braelist[c] = 0;
	}
	locs = 0;

	/*
	 * Check for NULL string, as in when backup
	 * register table has a line with only a # character
	 * in column 1.
	 */
	if (string == NULL)
		return(0);

	return( step( (char *)string, (char *)expbuf ) );
}

int
TLs_replace( tid, entry, fieldname, pattern, replacetxt )
int tid;
entry_t *entry;
unsigned char *fieldname, *pattern, *replacetxt;
{
	register tbl_t *tptr = TLtables + tid;
	register offset;
	unsigned char buf[1], *fieldvalue;
	if( !Strcmp( fieldname, TLCOMMENT ) ) {
		if( !E_NFIELDS(entry) ) fieldvalue = E_COMMENT(entry);
		else return( TLFAILED );
	} else if( !Strcmp( fieldname, TLTRAILING ) )
		fieldvalue = E_COMMENT(entry);
	else if( (offset = TLf_find( &(tptr->fieldnames), fieldname ) ) == -1 )
		return( TLBADFIELD );
	else fieldvalue = *E_GETFIELD(entry, offset);

	if( !replacetxt ) {
		buf[0] = '\0';
		replacetxt = buf;
	}
	if( !TLsubstitute( fieldvalue, pattern, replacetxt ) )
		return( TLFAILED );

	/* Now 'new' field value is in TLgenbuf */
	return( TLf_assign( tid, entry, fieldname, TLgenbuf ) );
}
