DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T d

⟦8b9fbdc28⟧ TextFile

    Length: 22873 (0x5959)
    Types: TextFile
    Names: »ds_search.c«

Derivation

└─⟦3d0c2be1b⟧ Bits:30001254 ISODE-5.0 Tape
    └─⟦eba4602b1⟧ »./isode-5.0.tar.Z« 
        └─⟦d3ac74d73⟧ 
            └─⟦this⟧ »isode-5.0/quipu/ds_search.c« 

TextFile

/* ds_search.c - DSA search of the directory */

#ifndef lint
static char *rcsid = "$Header: /f/osi/quipu/RCS/ds_search.c,v 6.0 89/03/18 23:41:23 mrose Rel $";
#endif

/*
 * $Header: /f/osi/quipu/RCS/ds_search.c,v 6.0 89/03/18 23:41:23 mrose Rel $
 *
 *
 * $Log:	ds_search.c,v $
 * Revision 6.0  89/03/18  23:41:23  mrose
 * Release 5.0
 * 
 */

/*
 *                                NOTICE
 *
 *    Acquisition, use, and distribution of this module and related
 *    materials are subject to the restrictions of a license agreement.
 *    Consult the Preface in the User's Manual for the full terms of
 *    this agreement.
 *
 */


#include "quipu/util.h"
#include "quipu/entry.h"
#include "quipu/list.h"      /* to get LSR # defs */
#include "quipu/ds_search.h"
#include "config.h"

extern LLog * log_dsap;

#ifndef NO_STATS
extern LLog * log_stat;
extern int dn_print ();
#endif


#define ASPE as->attr_value->avseq_av

static EntryInfo *filterentry();
static EntryInfo *filterchildren();
static test_avs();
static apply_search();
static soundex_search ();
static substr_search();
static aux_substr_search();
static check_filteritem_presrch ();
static check_filter_presrch ();
static check_filterop_presrch ();
static check_filteritem ();
static check_filter ();
static check_filterop ();
struct ds_search_task * st_done ();
static search_refer ();
static do_alias ();
Entry list_find_entry();

extern Entry database_root;
static int size;
static time_t srtime;
static time_t cnttime;
extern time_t time();
static DN binddn;
static DN path;
extern DN super_user;
extern int search_level;

Attr_Sequence eis_select ();

do_ds_search (arg, error, result, dnbind, target, local, refer)
    register struct ds_search_arg        *arg;
    struct ds_search_result     *result;
    struct DSError              *error;
    DN                          dnbind;
    DN                          target;
    struct ds_search_task       ** local,
			 	** refer;
{
extern time_t admin_time;
extern int admin_size;

	if (*local == NULL_ST) {
		DLOG (log_dsap,LLOG_TRACE,("ds_search"));

	if (target == NULLDN)
		target = arg->sra_baseobject;

#ifndef NO_STATS
		dn_decode (target);
		switch (arg->sra_subset) {
		case SRA_ONELEVEL:
			pslog (log_stat,LLOG_NOTICE,"search (onelevel)",dn_print,(caddr_t)target);
			break;
		case SRA_WHOLESUBTREE:	
			pslog (log_stat,LLOG_NOTICE,"search (subtree)",dn_print,(caddr_t)target);
			break;
		default:	
			pslog (log_stat,LLOG_NOTICE,"search (baseobject)",dn_print,(caddr_t)target);
			break;
		}
#endif
		/* Put local stuff straight into result structure (dangerous) */
		result->srr_correlated = TRUE;
		result->srr_un.srr_unit = (struct ds_search_unit *) calloc(1, sizeof(struct ds_search_unit));

		*local = st_alloc();
		(*local)->st_baseobject = dn_cpy (target);
		(*local)->st_subset = arg->sra_subset;
		(*local)->st_alias = NULLDN;
		if (((*local)->st_size = MIN(admin_size,arg->sra_common.ca_servicecontrol.svc_sizelimit)) == SVC_NOSIZELIMIT)
			(*local)->st_size = admin_size;
		if (((*local)->st_time = MIN(admin_time,arg->sra_common.ca_servicecontrol.svc_timelimit)) == SVC_NOTIMELIMIT)
			(*local)->st_time = admin_time;
		(*local)->st_cr = NULLCONTINUATIONREF;
		(*local)->st_next = NULL_ST;

		result->CSR_entries = NULLENTRYINFO;

		if (check_filter_presrch (arg->sra_filter,error) != OK)
			return (DS_ERROR_REMOTE);
		else {
			Entry entryptr;
			if (arg->sra_subset == SRA_BASEOBJECT)
				entryptr = find_entry ((*local)->st_baseobject,&arg->sra_common,error,dnbind);
			else
				entryptr = list_find_entry ((*local)->st_baseobject,&arg->sra_common,error,dnbind);
			if (entryptr != NULLENTRY) {
				/* if no error and NOT SVC_OPT_DONTDEREFERENCEALIASES then */
				/* the alias will have been derefeferenced -signified by   */
				/* NO_ERROR !!! */
				if (error->dse_type == DSE_NOERROR) {
					result->CSR_object = NULLDN;
					result->CSR_common.cr_aliasdereferenced =  FALSE;
				} else {
					result->CSR_common.cr_aliasdereferenced =  TRUE;
					result->CSR_object = get_copy_dn (entryptr);
				}

				/* one final check - will we allow such searched in this DSA ? */
				if (arg->sra_subset == SRA_WHOLESUBTREE) {
					DN dn;
					int x = 0;
					for (dn = (*local)->st_baseobject; dn!= NULLDN; dn=dn->dn_parent, x++)
						;
					if ( x < search_level ) {
						if (dn_cmp (dnbind,super_user) != OK) {
							/* Too high */
							error->dse_type = DSE_SERVICEERROR;
							error->ERR_SERVICE.DSE_sv_problem = DSE_SV_UNWILLINGTOPERFORM;
							return (DS_ERROR_REMOTE);
						}
					}
				}
				
				return (DS_CONTINUE); /* yup - we will take the search on */
			} else {
				if (error->dse_type == DSE_REFERRAL)
					return (DS_ERROR_CONNECT);
						/* can't co-ordinate it */
				else
					return (DS_ERROR_REMOTE);
			}
		}
	} else {
		DLOG (log_dsap,LLOG_NOTICE,("ds_search continuing"));
		binddn = dnbind;

		size = (*local)->st_size;
		srtime = (*local)->st_time;
		cnttime = time((time_t *)0);

		if ((*local)->st_alias == NULLDN)
			path = (*local)->st_baseobject;
		else
			path = (*local)->st_alias;

		if ((apply_search (arg,error,result,local,refer)) == NOTOK) {
			st_free (local);
			st_free (refer);
			return (DS_ERROR_REMOTE); 
		}

		if (cnttime < (time((time_t *)0) - srtime)) {
			st_free (local);
			st_free (refer);
			result->CSR_limitproblem = LSR_TIMELIMITEXCEEDED;
			return (DS_OK);
		}
		if (size <= 0) {
			st_free (local);
			st_free (refer);
			if (arg->sra_common.ca_servicecontrol.svc_sizelimit == SVC_NOSIZELIMIT)
				result->CSR_limitproblem = LSR_ADMINSIZEEXCEEDED;
			else if (admin_size < arg->sra_common.ca_servicecontrol.svc_sizelimit)
				result->CSR_limitproblem = LSR_ADMINSIZEEXCEEDED;
			else
				result->CSR_limitproblem = LSR_SIZELIMITEXCEEDED;
			return (DS_OK);
		}
			

		if ((*local)->st_next == NULL_ST) {
			st_free (local);
			return (DS_OK);
		} else {
			(*local) = st_done(local);
			(*local)->st_size = size;
			(*local)->st_time = srtime - (time((time_t *)0) - cnttime);
			return (DS_CONTINUE);
		}
	}

}

/* 
 * SEARCH TASK HANDLING 
 */

st_comp_free (st)
struct ds_search_task *st;
{
	dn_free (st->st_baseobject);
	dn_free (st->st_alias);
	crefs_free (st->st_cr);
	free (st);
}

st_free (st)
struct ds_search_task **st;
{
	for (; (*st) != NULL_ST; (*st) = (*st)->st_next)
		st_comp_free (*st);
}

struct ds_search_task * st_done (st)
struct ds_search_task **st;
{
struct ds_search_task *next;
	
	next = (*st)->st_next;
	st_comp_free (*st);
	return (next);
}



/*
 * CHECK FILTER BEFORE SEARCHING 
 */


static check_filter_presrch (fltr,error)
    register Filter  fltr;
    struct DSError *error;
{
	DLOG (log_dsap,LLOG_DEBUG,("in check filter aux"));

	switch (fltr->flt_type) {
	    case FILTER_ITEM:
		return (check_filteritem_presrch (&fltr->FUITEM,error));
	    case FILTER_AND:
	    case FILTER_OR:
		return(check_filterop_presrch (fltr->FUFILT,error));
	    case FILTER_NOT:
		return(check_filter_presrch (fltr->FUFILT,error));
	    default:
		LLOG (log_dsap,LLOG_EXCEPTIONS,("check_filter protocol error"));
		error->dse_type = DSE_SERVICEERROR;
		error->ERR_SERVICE.DSE_sv_problem = DSE_SV_UNAVAILABLE;
		return (NOTOK);
		}
	/* NOTREACHED */
}

static check_filterop_presrch (fltr,error)
    register Filter  fltr;
    struct DSError * error;
{
register Filter ptr;
int i;

	DLOG (log_dsap,LLOG_DEBUG,("in filter op aux"));
	for (ptr=fltr; ptr!=NULLFILTER ; ptr=ptr->flt_next) {
		i = check_filter_presrch (ptr,error);
		if (i != OK)
			return (NOTOK);
	}
	return (OK);

}


static check_filteritem_presrch (fitem,error)
    register struct filter_item *fitem;
    struct DSError * error;
{
	DLOG (log_dsap,LLOG_DEBUG,("search: check filter item aux"));
	if (fitem == NULLFITEM) {
		LLOG (log_dsap,LLOG_EXCEPTIONS,("check_filter_item protocol error (1)"));
		error->dse_type = DSE_SERVICEERROR;
		error->ERR_SERVICE.DSE_sv_problem = DSE_SV_UNAVAILABLE;
		return (NOTOK);
	}

	switch ( fitem->fi_type) {
	    case FILTERITEM_APPROX:
		ava_decode (&fitem->UNAVA);
		AttrT_decode (fitem->UNAVA.ava_type);
		if (fitem->UNAVA.ava_type->at_table == NULLTABLE_ATTR)
			return (invalid_matching (fitem->UNAVA.ava_type,error));
		if ((fitem->UNAVA.ava_type->at_table->oa_syntax== AV_CASEEXACTSTRING) || (fitem->UNAVA.ava_type->at_table->oa_syntax == AV_CASEIGNORESTRING)) {
			fitem_soundex_key (fitem);
			break;
		} else
				/* approx not suported for this type */
				/* so set it to equality */
			fitem->fi_type = FILTERITEM_EQUALITY;
				/* NO break - check equality is OK */

	    case FILTERITEM_EQUALITY:
	    case FILTERITEM_GREATEROREQUAL:
	    case FILTERITEM_LESSOREQUAL:
		AttrT_decode(fitem->UNAVA.ava_type);
		ava_decode (&fitem->UNAVA);
		if (fitem->UNAVA.ava_type->at_table == NULLTABLE_ATTR)
			return (invalid_matching (fitem->UNAVA.ava_type,error));
		switch (fitem->UNAVA.ava_type->at_table->oa_syntax) {
			case AV_ACL:
			case AV_SCHEMA:
			case AV_UPDATE:
				/* if matching written, allow the match */
				return (invalid_matching (fitem->UNAVA.ava_type,error));
		}
		break;
	    case FILTERITEM_SUBSTRINGS:
	        AttrT_decode (fitem->UNSUB.fi_sub_type);
		avs_decode (fitem->UNSUB.fi_sub_type, fitem->UNSUB.fi_sub_initial);
		avs_decode (fitem->UNSUB.fi_sub_type, fitem->UNSUB.fi_sub_any);
		avs_decode (fitem->UNSUB.fi_sub_type, fitem->UNSUB.fi_sub_final);

		if (fitem->UNSUB.fi_sub_type->at_table == NULLTABLE_ATTR)
			return (invalid_matching (fitem->UNSUB.fi_sub_type,error));
		switch (fitem->UNSUB.fi_sub_type->at_table->oa_syntax) {
			case AV_CASEEXACTSTRING:
			case AV_CASEIGNORESTRING:
				break;
			default:
				return (invalid_matching (fitem->UNSUB.fi_sub_type,error));
		}
	    case FILTERITEM_PRESENT:
		break;
	    default:
		LLOG (log_dsap,LLOG_EXCEPTIONS,("check_filter_item protocol error (2)"));
		error->dse_type = DSE_SERVICEERROR;
		error->ERR_SERVICE.DSE_sv_problem = DSE_SV_UNAVAILABLE;
		return (NOTOK);
	}
	return (OK);
}


/* APPLY SEARCH TO ONE LEVEL */

static apply_search (arg,error,result,local,refer)
    struct ds_search_arg	*arg;
    struct DSError              *error;
    struct ds_search_result     *result;
    struct ds_search_task	**local,
			        **refer;
{
Entry entryptr;
EntryInfo  *einfo = NULLENTRYINFO;

	if ((*local)->st_subset == SRA_BASEOBJECT)
		entryptr = find_entry ((*local)->st_baseobject,&arg->sra_common,error,binddn);
	else
		entryptr = list_find_entry ((*local)->st_baseobject,&arg->sra_common,error,binddn);

	if (entryptr != NULLENTRY) {
		switch ((*local)->st_subset) {
		case SRA_BASEOBJECT:
			einfo = filterentry (arg,entryptr);
			break;
		case SRA_ONELEVEL:
		case SRA_WHOLESUBTREE:	
			einfo = filterchildren (arg,entryptr,local,refer,(*local)->st_subset);
			break;
		default:
			LLOG (log_dsap,LLOG_EXCEPTIONS,("search protocol error"));
			error->dse_type = DSE_SERVICEERROR;
			error->ERR_SERVICE.DSE_sv_problem = DSE_SV_UNAVAILABLE;
			return (NOTOK);
		}

		if (einfo != NULLENTRYINFO)
			if (result->CSR_entries == NULLENTRYINFO)
				result->CSR_entries = einfo;
			else
				entryinfo_append (result->CSR_entries,einfo);


		result->CSR_common.cr_requestor = NULLDN;
		result->CSR_cr = NULLCONTINUATIONREF;

		if (size < 0)
			result->CSR_limitproblem = LSR_SIZELIMITEXCEEDED;
		else
			result->CSR_limitproblem = LSR_NOLIMITPROBLEM;

		return (OK);
	
	} 

	if (error->dse_type == DSE_REFERRAL) {
		/* turn query into a referral */
		struct ds_search_task	* new_task;
		
		new_task = st_alloc();
		new_task->st_baseobject = dn_cpy ((*local)->st_baseobject);
		new_task->st_subset = (*local)->st_subset;
		new_task->st_alias = dn_cpy ((*local)->st_baseobject);
		if ((new_task->st_size = MIN(admin_size,arg->sra_common.ca_servicecontrol.svc_sizelimit)) == SVC_NOSIZELIMIT)
			new_task->st_size = admin_size;
		if ((new_task->st_time = MIN(admin_time,arg->sra_common.ca_servicecontrol.svc_timelimit)) == SVC_NOTIMELIMIT)
			new_task->st_time = admin_time;
		new_task->st_cr = error->ERR_REFERRAL.DSE_ref_candidates;
		new_task->st_next = *refer;
		*refer = new_task;
		return (OK);
		}

	
	return (NOTOK);
}

/* 
 * SEARCH CHILDREN
 */

static EntryInfo * filterchildren (arg,entryptr,local,refer,extent)
    struct ds_search_arg        *arg;
    Entry  entryptr;
    struct ds_search_task	**local,
			        **refer;
    int    extent;
{
register EntryInfo  *einfo = NULLENTRYINFO;
register EntryInfo  *eptr = NULLENTRYINFO;
register Entry ptr;
DN dnend, dnsave;
RDN freeend;
struct ds_search_task	* new_task;

	DLOG (log_dsap,LLOG_DEBUG,("search: filter children"));

	if (entryptr == NULLENTRY)
		return (NULLENTRYINFO);

	if (entryptr->e_leaf)
		return (NULLENTRYINFO);

	if (check_acl (binddn, ACL_READ, entryptr->e_acl->ac_child, (*local)->st_baseobject) == NOTOK) {
		return (NULLENTRYINFO);
	}

	if (( ptr=entryptr->e_child ) == NULLENTRY) {
		search_refer (arg,entryptr,local,refer);
		return (NULLENTRYINFO);
	}

	if (path == NULLDN) {
		path = dn_comp_new(rdn_cpy(entryptr->e_child->e_name));
		freeend = path->dn_rdn;
		dnend = path;
	} else {
		for (dnend=path; dnend->dn_parent != NULLDN; dnend=dnend->dn_parent)
			; /* NULL */
		dnend->dn_parent = dn_comp_new(rdn_cpy(entryptr->e_child->e_name));
		freeend = dnend->dn_parent->dn_rdn;
		dnsave = dnend;
		dnend = dnend ->dn_parent;
	}

	for (; (ptr != NULLENTRY) && (size > 0) ; ptr=ptr->e_sibling) {
		dnend->dn_rdn = ptr->e_name;
		if (ptr->e_alias != NULLDN) {
			do_alias (arg,ptr,local);
			continue;
		} else
			eptr = filterentry (arg,ptr);

		if (eptr != NULLENTRYINFO)
			if (einfo == NULLENTRYINFO)
				einfo =  eptr;
			else
				entryinfo_append (einfo,eptr);

		if ( (extent == SRA_WHOLESUBTREE) && (!ptr->e_leaf)) {
			if (check_acl (binddn, ACL_READ, ptr->e_acl->ac_child, path) == OK) {
				if (((ptr->e_child != NULLENTRY) && (ptr->e_allchildrenpresent == FALSE)) 
						|| (ptr->e_child == NULLENTRY))
					search_refer (arg,ptr,local,refer);
				else {
					new_task = st_alloc();
					new_task->st_baseobject = dn_cpy(path);
					new_task->st_size = 0;	/* fill in later */
					new_task->st_time = 0;
					new_task->st_alias = NULLDN;
					new_task->st_cr = NULLCONTINUATIONREF;
					new_task->st_subset = SRA_WHOLESUBTREE;
					new_task->st_next = (*local)->st_next;
					(*local)->st_next = new_task;
				}
			}
		}
	}

	if (path->dn_parent == NULLDN) {
		path->dn_rdn = NULLRDN;
		dn_free (path);
		path = NULLDN;
	} else {
		dnend->dn_rdn = NULLRDN;
		dn_free (dnend);
		dnsave->dn_parent = NULLDN;
	}
	rdn_free (freeend);
	return (einfo);
}

/* 
 * HANDLE ALIASES AND REFERRALS
 */

static do_alias (arg,eptr,local)
    struct ds_search_arg        *arg;
    Entry eptr;
    struct ds_search_task	**local;
{
struct ds_search_task *new_task;

	if ( (arg->sra_common.ca_servicecontrol.svc_options & SVC_OPT_DONTDEREFERENCEALIAS) != 0)
		return;

	DLOG (log_dsap,LLOG_DEBUG,("alias in search path"));

	new_task = st_alloc();
	new_task->st_baseobject = dn_cpy(eptr->e_alias);
	new_task->st_size = 0;	/* fill in later */
	new_task->st_time = 0;
	new_task->st_alias = get_copy_dn (eptr);
	new_task->st_cr = NULLCONTINUATIONREF;

	switch ((*local)->st_subset) {
	case SRA_ONELEVEL:
	case SRA_BASEOBJECT:
		new_task->st_subset = SRA_BASEOBJECT;
		break;
	case SRA_WHOLESUBTREE:
		new_task->st_subset = SRA_WHOLESUBTREE;
		break;
	}

	new_task->st_next = (*local)->st_next;
	(*local)->st_next = new_task;

}

static search_refer(arg,entryptr,local,refer)
    struct ds_search_arg        *arg;
    Entry  entryptr;
    struct ds_search_task	**local,
			        **refer;
{
struct ds_search_task *new_task;
ContinuationRef ref,cont_ref_new();	
DN name;	

	name = get_copy_dn (entryptr);

	if ((ref = cont_ref_new (name,entryptr)) == NULLCONTINUATIONREF) {
		dn_free (name);
		return;   /* leaf or invalid reference !!! */
	}

	DLOG (log_dsap,LLOG_DEBUG,("referral in search path"));

	new_task = st_alloc();
	new_task->st_baseobject = name;
	new_task->st_subset = (*local)->st_subset;
	new_task->st_alias = NULLDN;
	if ((new_task->st_size = MIN(admin_size,arg->sra_common.ca_servicecontrol.svc_sizelimit)) == SVC_NOSIZELIMIT)
		new_task->st_size = admin_size;
	if ((new_task->st_time = MIN(admin_time,arg->sra_common.ca_servicecontrol.svc_timelimit)) == SVC_NOTIMELIMIT)
		new_task->st_time = admin_time;
	new_task->st_cr = ref;
	new_task->st_next = *refer;
	*refer = new_task;
}

/*
 * SEARCH ENTRY
 */  

static EntryInfo * filterentry (arg,entryptr)
    struct ds_search_arg        *arg;
    register Entry entryptr;
{
register EntryInfo * einfo;
register Attr_Sequence  as;

	DLOG (log_dsap,LLOG_DEBUG,("search: filter entry"));

	if (check_filter (arg->sra_filter,entryptr) != OK ) {
		DLOG (log_dsap,LLOG_DEBUG,("none found"));
		return (NULLENTRYINFO);
	}

	as = eis_select (arg->sra_eis, entryptr, binddn);

	einfo = entryinfo_alloc ();
	einfo->ent_dn = dn_cpy (path);
	einfo->ent_attr = as;
	einfo->ent_iscopy = entryptr->e_data;
	einfo->ent_age = (time_t) 0;
	einfo->ent_next = NULLENTRYINFO;
	size--;
	return (einfo);
}

/* 
 * TEST FILTER AGAINST SINGLE ENTRY
 */

static check_filter (fltr,entryptr)
    register Filter  fltr;
    register Entry  entryptr;
{
register int i;

	DLOG (log_dsap,LLOG_DEBUG,("in check filter"));
	switch (fltr->flt_type) {
	    case FILTER_ITEM:
		return (check_filteritem (&fltr->FUITEM,entryptr));
	    case FILTER_AND:
	    case FILTER_OR:
		return(check_filterop (fltr->FUFILT,entryptr,fltr->flt_type));
	    case FILTER_NOT:
		i = check_filter (fltr->FUFILT,entryptr);
		if ( i == -2)
			return (-2);
		else if (i == OK)
			return (NOTOK);
		else
			return (OK);
		}
	/* NOTREACHED */
}

static check_filterop (fltr,entryptr,op)
    register Filter  fltr;
    register Entry  entryptr;
    int op;
{
register Filter ptr;

	DLOG (log_dsap,LLOG_DEBUG,("in filter op"));
	for (ptr=fltr; ptr!=NULLFILTER ; ptr=ptr->flt_next)
		switch (check_filter (ptr,entryptr)) {
			case -2:
				return (-2);
			case 0:
				if (op == FILTER_OR) {
					DLOG (log_dsap,LLOG_DEBUG,("or ok"));
					return (OK);
				}
				break;
			default:
				if (op == FILTER_AND) {
					DLOG (log_dsap,LLOG_DEBUG,("and not"));
					return (NOTOK);
				}
				break;
		}

	if (op == FILTER_OR)
		return (NOTOK);
	else
		return (OK);

}

/* 
 * CHECK FILTER ITEM AGAINST ENTRY 
 */

static check_filteritem (fitem,entryptr)
    register struct filter_item *fitem;
    register Entry  entryptr;
{
register Attr_Sequence as;
AttributeType at;

	DLOG (log_dsap,LLOG_DEBUG,("search: check filter item"));

	switch ( fitem->fi_type) {
	    case FILTERITEM_APPROX:
	    case FILTERITEM_EQUALITY:
	    case FILTERITEM_GREATEROREQUAL:
	    case FILTERITEM_LESSOREQUAL:
		at = fitem->UNAVA.ava_type;
		break;
	    case FILTERITEM_SUBSTRINGS:
		at = fitem->UNSUB.fi_sub_type;
		break;
	    case FILTERITEM_PRESENT:
		at = fitem->UNTYPE;
		break;
	}

	if ((as = as_find_type (entryptr->e_attributes, at)) == NULLATTR) 
		return (NOTOK);

	switch ( fitem->fi_type) {
	    case FILTERITEM_SUBSTRINGS:
		return (substr_search (fitem,as->attr_value));
	    case FILTERITEM_APPROX:
		return (soundex_search  (fitem,as->attr_value));
	    case FILTERITEM_PRESENT:
		return (OK);
	    default:
		return (test_avs (fitem,as->attr_value,fitem->fi_type));
	}
	/* NOTREACHED */
}

static test_avs (fitem,avs,mode)
    register struct filter_item *fitem;
    register AV_Sequence avs;
    register int mode;
{
register int i;
struct DSError error;

	for (; avs != NULLAV; avs=avs->avseq_next) {
		i = AttrV_cmp (avs->avseq_av, fitem->UNAVA.ava_value,&error);
		switch (i) {
			case 0:
				return (OK);
			case -1:
				if (mode == FILTERITEM_GREATEROREQUAL)
					return (OK);
				break;
			case 1:
				if (mode == FILTERITEM_LESSOREQUAL)
					return (OK);
				break;
			default:
				ds_error_free (&error);
				/* not interested in errs here */
		}
	}
	return (NOTOK);
}


/* 
 * APPROX SEARCH
 */

static soundex_search (fitem,avs)
    register struct filter_item *fitem;
    register AV_Sequence avs;
{
	for (; avs != NULLAV; avs=avs->avseq_next)
		if (soundex_cmp (avs->avseq_soundex, fitem->fi_soundex) == OK)
			return (OK);

	return (NOTOK);
}

/*
 * SUBSTRING MATCH 
 */

static substr_search (fitem,avs)
    register struct filter_item *fitem;
    register AV_Sequence avs;
{
extern char chrcnv[];
extern char nochrcnv[];

	for (; avs != NULLAV; avs=avs->avseq_next)
		switch (avs->avseq_av->av_syntax) {
		    case AV_CASEEXACTSTRING:
			if (aux_substr_search (fitem,avs,nochrcnv) == OK)
				return (OK);
			break;
		    case AV_CASEIGNORESTRING:
			if (aux_substr_search (fitem,avs,chrcnv) == OK)
				return (OK);
			break;
		}
	return (NOTOK);
}



static aux_substr_search (fitem,avs,chrmatch)
    struct filter_item *fitem;
    AV_Sequence avs;
    char chrmatch [];
{
register AV_Sequence loopavs;
register char * compstr;
char * top;
register char * temp;
char * temp2;
int offset;

	compstr = avs->avseq_av->av_un.av_str;
	top  = compstr;
	if (fitem->UNSUB.fi_sub_initial != NULLAV) {
		temp = fitem->UNSUB.fi_sub_initial->avseq_av->av_un.av_str;
		do
			if (chrmatch[*compstr++] != chrmatch[*temp++]) {
				DLOG (log_dsap,LLOG_DEBUG,("initial failure (%s, %s)",top,fitem->UNSUB.fi_sub_initial->avseq_av->av_un.av_str));
				return (NOTOK);
			}
		while (*temp != '\0') ;
	}

	for (loopavs=fitem->UNSUB.fi_sub_any; loopavs!=NULLAV; loopavs=loopavs->avseq_next, compstr += offset)
		if ((offset= attr_substr (compstr, loopavs->avseq_av,chrmatch)) == -1) {
			DLOG (log_dsap,LLOG_DEBUG,("any failure (%s, %s)",top,loopavs->avseq_av->av_un.av_str));
			return (NOTOK);
		}

	if (fitem->UNSUB.fi_sub_final != NULLAV) {
		temp = fitem->UNSUB.fi_sub_final->avseq_av->av_un.av_str;
		temp2 = temp;
		while (*++compstr != '\0')
			;  /* NO-OP*/

		while (*temp++ != '\0')
			compstr--;

		if (compstr < top) {
			DLOG (log_dsap,LLOG_DEBUG,("final too long failure (%s,%s)",top,temp2));
			return (NOTOK);
		}

		temp = temp2;
		while (*compstr != '\0')
			if (chrmatch[*compstr++] != chrmatch[*temp++]) {
				/* free (top); */
				DLOG (log_dsap,LLOG_DEBUG,("final failure (%s, %s)",top,temp2));
				return (NOTOK);
			}
	}
	return (OK);
}

attr_substr (str1,av,chrmatch)
register char * str1;
AttributeValue av;
char chrmatch[];
{
register char * str2;
register int count;
char * top;
int found = 0;
int slen;

    top = str1;
    str2 = av->av_un.av_str;

    while (*str1 != '\0') {
	if (chrmatch[*str1++] == chrmatch[*str2]) {
		str2++;
		found = 1;
		break;
	}
    }

    if ( found == 0 )
	return (-1);

    slen = strlen (str2) + 1;
    for (count = 1; count < slen ; count ++) {
	if (*str1 == '\0')
	    return (-1);

	if (chrmatch[*str1++] != chrmatch[*str2++]) {
		/* not found here, but may still be in the string !! */
		str1 -= count;
		str2 -= count + 1;
		while (*str1 != '\0') {
			if (chrmatch[*str1++] == chrmatch[*str2]) {
				str2++;
				break;
			}
		}
		count = 0;  /* for loop ++ will make it 1 !!! */
	}
    }
    return (str1 - top);
}