/*
 * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis
 * All rights reserved.  The file named COPYRIGHT specifies the terms 
 * and conditions for redistribution.
 */

static char RCSid[] = "strs.c,v 1.1.1.1 1994/04/26 19:08:12 hardy Exp" ;

#include <ctype.h>
#include <memory.h>

char *malloc() ;

#include "ss_impl.h"

extern struct ss_ops __strs_bfops ;
extern struct ss_ops __strs_rkops ;
extern struct ss_ops __strs_kmpops ;
extern struct ss_ops __strs_sbmops ;
extern struct ss_ops __strs_bmhops ;

/*
 * NOTE: This table is arranged according to increasing method number.
 *			This allows quick indexing into it using the user-provided
 *			method as a hint:
 *				if ( selection_table[ user_method ].method == user_method )
 *					FOUND
 *				else
 *					DO SEQUENTIAL SEARCH
 *			This allows both quick access and a change of method numbers
 *			in the future without requiring recompilation of programs in
 *			order to work with new versions of the library.
 */
static struct ss_select selection_table[] =
	{
		{ STRS_BF,					&__strs_bfops		},
		{ STRS_RK,					&__strs_rkops		},
		{ STRS_KMP,					&__strs_kmpops		},
		{ STRS_SBM,					&__strs_sbmops		},
		{ STRS_BMH,					&__strs_bmhops		},
		{ 0,							0						}
	} ;

char identity_map[ ALPHABET_SIZE ] ;
char upper_to_lower_map[ ALPHABET_SIZE ] ;

static int tables_initialized ;

/*
 * This header is returned when an empty pattern is given to strs_setup.
 * The rest of the functions check ss_patlen and do nothing if that is zero.
 * ss_patlen in this header will be initialized to zero.
 */
static header_s empty_pattern_header ;


PRIVATE void initialize_tables()
{
	int i ;

	for ( i = 0 ; i < sizeof( upper_to_lower_map ) ; i++ )
	{
		if ( isascii( i ) && isupper( i ) )
			upper_to_lower_map[ i ] = i + 'a' - 'A' ;
		else
			upper_to_lower_map[ i ] = i ;
		identity_map[ i ] = i ;
	}
}


/*
 * Initializes header
 *
 * Note that 'pattern' does not need to be a NUL-terminated string.
 */
PRIVATE int init( hp, flags, pattern, patlen )
	register header_s *hp ;
	int flags ;
	char *pattern ;
	int patlen ;
{
	int requested_method = SS_GETMETHOD( flags ) ;
	register struct ss_select *selp ;

	if ( ! tables_initialized )
	{
		initialize_tables() ;
		tables_initialized = TRUE ;
	}

	/*
	 * Determine ops
	 */
	if ( selection_table[ requested_method ].sel_method == requested_method )
		selp = &selection_table[ requested_method ] ;
	else
		for ( selp = &selection_table[ 0 ] ;; selp++ )
		{
			if ( ! selp->sel_ops )
				return( SS_ERR ) ;
			if ( requested_method == selp->sel_method )
				break ;
		}

	/*
	 * Initialize header fields
	 */
	SS_OPS( hp ) = selp->sel_ops ;
	SS_FLAGS( hp ) = SS_GETFLAGS( flags ) ;
	SS_PATLEN( hp ) = patlen ;

	if ( SS_MALLOC( hp ) )
	{
		SS_PATTERN( hp ) = malloc( (unsigned)SS_PATLEN( hp ) + 1 ) ;
		if ( SS_PATTERN( hp ) == CHAR_NULL )
		{
			(void) free( (char *)hp ) ;
			return( SS_ERR ) ;
		}
		(void) memcpy( SS_PATTERN( hp ), pattern, (int)SS_PATLEN( hp )+1 ) ;
	}
	else
		SS_PATTERN( hp ) = pattern ;

	/*
	 * If the user asked for case-insensitive search, we create our own
	 * copy of the pattern in lower case. If the pattern is malloc'ed
	 * we overwrite, otherwise we malloc some memory and clear the
	 * STRS_NOMALLOC flag.
	 */
	if ( SS_IGNCASE( hp ) )
	{
		char *new_pattern ;
		register int i ;

		SS_SETMAP( hp, upper_to_lower_map ) ;

		if ( SS_MALLOC( hp ) )
			new_pattern = SS_PATTERN( hp ) ;
		else
		{
			new_pattern = malloc( (unsigned)SS_PATLEN( hp ) + 1 ) ;
			if ( new_pattern == CHAR_NULL )
				return( SS_ERR ) ;
			SS_SETMALLOC( hp ) ;			/* clears the STRS_NOMALLOC flag */
		}
		for ( i = 0 ; i < SS_PATLEN( hp ) ; i++ )
			new_pattern[ i ] = SS_MAP( hp, SS_PATTERN( hp )[ i ] ) ;
		SS_PATTERN( hp ) = new_pattern ;
	}
	else
		SS_SETMAP( hp, identity_map ) ;

	if ( SS_SETUP( hp ) == SS_OK )
		return( SS_OK ) ;
	else
	{
		if ( SS_MALLOC( hp ) )
			(void) free( (char *)hp ) ;
		return( SS_ERR ) ;
	}
}


/*
 * Finalize header
 */
PRIVATE void fini( hp )
	header_s *hp ;
{
	SS_DONE( hp ) ;
	if ( SS_MALLOC( hp ) )
		(void) free( SS_PATTERN( hp ) ) ;
}


/*
 * Create a search handle
 */
strs_h strs_setup( flags, pattern )
	int flags ;
	char *pattern ;
{
	header_s *hp ;
	int patlen ;

	hp = HP( malloc( sizeof( *hp ) ) ) ;
	if ( hp == NULL )
		return( NULL_HANDLE ) ;
	
	patlen = strlen( pattern ) ;
	if ( patlen == 0 )
		return( (strs_h) &empty_pattern_header ) ;

	if ( init( hp, flags, pattern, patlen ) == SS_OK )
		return( (strs_h)hp ) ;
	else
	{
		free( (char *)hp ) ;
		return( NULL_HANDLE ) ;
	}
}


/*
 * Destroy a search handle
 */
void strs_done( handle )
	void *handle ;
{
	header_s *hp = HP( handle ) ;

	if ( SS_PATLEN( hp ) != 0 )
	{
		fini( hp ) ;
		(void) free( (char *) handle ) ;
	}
}


char *strs_match( handle, str, len )
	strs_h handle ;
	char *str ;
	int len ;
{
	register header_s *hp = HP( handle ) ;

	if ( SS_PATLEN( hp ) == 0 )
		return( str ) ;
	if ( SS_PATLEN( hp ) > len )
		return( CHAR_NULL ) ;
	return( SS_MATCH( hp, str, len ) ) ;
}



char *strs_search( flags, str, len, pattern )
	int flags ;
	char *str ;
	int len ;
	char *pattern ;			/* NUL-terminated */
{
	header_s t_header ;
	char *p ;
	int patlen = strlen( pattern ) ;

	if ( patlen == 0 )
		return( str ) ;

	if ( patlen > len )
		return( CHAR_NULL ) ;

	if ( init( &t_header, flags | STRS_NOMALLOC, pattern, patlen ) == SS_OK )
	{
		p = SS_MATCH( &t_header, str, len ) ;
		fini( &t_header ) ;
		return( p ) ;
	}
	else
		return( CHAR_NULL ) ;
}


