filter2bv_undef_x( Operation *op, Filter *f, int noundef, struct berval *fstr )
{
	int		i;
	Filter		*p;
	struct berval	tmp, value;
	static struct berval
			ber_bvfalse = BER_BVC( "(?=false)" ),
			ber_bvtrue = BER_BVC( "(?=true)" ),
			ber_bvundefined = BER_BVC( "(?=undefined)" ),
			ber_bverror = BER_BVC( "(?=error)" ),
			ber_bvunknown = BER_BVC( "(?=unknown)" ),
			ber_bvnone = BER_BVC( "(?=none)" ),
			ber_bvF = BER_BVC( "(|)" ),
			ber_bvT = BER_BVC( "(&)" );
	ber_len_t	len;
	ber_tag_t	choice;
	int undef, undef2;
	char *sign;

	if ( f == NULL ) {
		ber_dupbv_x( fstr, &ber_bvnone, op->o_tmpmemctx );
		return;
	}

	undef = f->f_choice & SLAPD_FILTER_UNDEFINED;
	undef2 = (undef && !noundef);
	choice = f->f_choice & SLAPD_FILTER_MASK;

	switch ( choice ) {
	case LDAP_FILTER_EQUALITY:
		fstr->bv_len = STRLENOF("(=)");
		sign = "=";
		goto simple;
	case LDAP_FILTER_GE:
		fstr->bv_len = STRLENOF("(>=)");
		sign = ">=";
		goto simple;
	case LDAP_FILTER_LE:
		fstr->bv_len = STRLENOF("(<=)");
		sign = "<=";
		goto simple;
	case LDAP_FILTER_APPROX:
		fstr->bv_len = STRLENOF("(~=)");
		sign = "~=";

simple:
		value = f->f_av_value;
		if ( f->f_av_desc->ad_type->sat_equality &&
			!undef &&
			( f->f_av_desc->ad_type->sat_equality->smr_usage & SLAP_MR_MUTATION_NORMALIZER ))
		{
			f->f_av_desc->ad_type->sat_equality->smr_normalize(
				(SLAP_MR_DENORMALIZE|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX),
				NULL, NULL, &f->f_av_value, &value, op->o_tmpmemctx );
		}

		filter_escape_value_x( &value, &tmp, op->o_tmpmemctx );
		/* NOTE: tmp can legitimately be NULL (meaning empty) 
		 * since in a Filter values in AVAs are supposed
		 * to have been normalized, meaning that an empty value
		 * is legal for that attribute's syntax */

		fstr->bv_len += f->f_av_desc->ad_cname.bv_len + tmp.bv_len;
		if ( undef2 )
			fstr->bv_len++;
		fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );

		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s)",
			undef2 ? "?" : "",
			f->f_av_desc->ad_cname.bv_val, sign,
			tmp.bv_len ? tmp.bv_val : "" );

		if ( value.bv_val != f->f_av_value.bv_val ) {
			ber_memfree_x( value.bv_val, op->o_tmpmemctx );
		}

		ber_memfree_x( tmp.bv_val, op->o_tmpmemctx );
		break;

	case LDAP_FILTER_SUBSTRINGS:
		fstr->bv_len = f->f_sub_desc->ad_cname.bv_len +
			STRLENOF("(=*)");
		if ( undef2 )
			fstr->bv_len++;
		fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 128, op->o_tmpmemctx );

		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s=*)",
			undef2 ? "?" : "",
			f->f_sub_desc->ad_cname.bv_val );

		if ( f->f_sub_initial.bv_val != NULL ) {
			ber_len_t tmplen;

			len = fstr->bv_len;

			filter_escape_value_x( &f->f_sub_initial, &tmp, op->o_tmpmemctx );
			tmplen = tmp.bv_len;

			fstr->bv_len += tmplen;
			fstr->bv_val = op->o_tmprealloc( fstr->bv_val,
				fstr->bv_len + 1, op->o_tmpmemctx );

			snprintf( &fstr->bv_val[len - 2],
				tmplen + STRLENOF( /*(*/ "*)" ) + 1,
				/* "(attr=" */ "%s*)",
				tmp.bv_len ? tmp.bv_val : "");

			ber_memfree_x( tmp.bv_val, op->o_tmpmemctx );
		}

		if ( f->f_sub_any != NULL ) {
			for ( i = 0; f->f_sub_any[i].bv_val != NULL; i++ ) {
				ber_len_t tmplen;

				len = fstr->bv_len;
				filter_escape_value_x( &f->f_sub_any[i],
					&tmp, op->o_tmpmemctx );
				tmplen = tmp.bv_len;

				fstr->bv_len += tmplen + STRLENOF( /*(*/ ")" );
				fstr->bv_val = op->o_tmprealloc( fstr->bv_val,
					fstr->bv_len + 1, op->o_tmpmemctx );

				snprintf( &fstr->bv_val[len - 1],
					tmplen + STRLENOF( /*(*/ "*)" ) + 1,
					/* "(attr=[init]*[any*]" */ "%s*)",
					tmp.bv_len ? tmp.bv_val : "");
				ber_memfree_x( tmp.bv_val, op->o_tmpmemctx );
			}
		}

		if ( f->f_sub_final.bv_val != NULL ) {
			ber_len_t tmplen;

			len = fstr->bv_len;

			filter_escape_value_x( &f->f_sub_final, &tmp, op->o_tmpmemctx );
			tmplen = tmp.bv_len;

			fstr->bv_len += tmplen;
			fstr->bv_val = op->o_tmprealloc( fstr->bv_val,
				fstr->bv_len + 1, op->o_tmpmemctx );

			snprintf( &fstr->bv_val[len - 1],
				tmplen + STRLENOF( /*(*/ ")" ) + 1,
				/* "(attr=[init*][any*]" */ "%s)",
				tmp.bv_len ? tmp.bv_val : "");

			ber_memfree_x( tmp.bv_val, op->o_tmpmemctx );
		}

		break;

	case LDAP_FILTER_PRESENT:
		fstr->bv_len = f->f_desc->ad_cname.bv_len +
			STRLENOF("(=*)");
		if ( undef2 )
			fstr->bv_len++;

		fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );

		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s=*)",
			undef2 ? "?" : "",
			f->f_desc->ad_cname.bv_val );
		break;

	case LDAP_FILTER_AND:
	case LDAP_FILTER_OR:
	case LDAP_FILTER_NOT:
		fstr->bv_len = STRLENOF("(%)");
		fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 128, op->o_tmpmemctx );

		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%c)",
			f->f_choice == LDAP_FILTER_AND ? '&' :
			f->f_choice == LDAP_FILTER_OR ? '|' : '!' );

		for ( p = f->f_list; p != NULL; p = p->f_next ) {
			len = fstr->bv_len;

			filter2bv_undef_x( op, p, noundef, &tmp );
			
			fstr->bv_len += tmp.bv_len;
			fstr->bv_val = op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1,
				op->o_tmpmemctx );

			snprintf( &fstr->bv_val[len-1],
				tmp.bv_len + STRLENOF( /*(*/ ")" ) + 1, 
				/*"("*/ "%s)", tmp.bv_val );

			op->o_tmpfree( tmp.bv_val, op->o_tmpmemctx );
		}

		break;

	case LDAP_FILTER_EXT: {
		struct berval ad;

		filter_escape_value_x( &f->f_mr_value, &tmp, op->o_tmpmemctx );
		/* NOTE: tmp can legitimately be NULL (meaning empty) 
		 * since in a Filter values in MRAs are supposed
		 * to have been normalized, meaning that an empty value
		 * is legal for that attribute's syntax */

		if ( f->f_mr_desc ) {
			ad = f->f_mr_desc->ad_cname;
		} else {
			ad.bv_len = 0;
			ad.bv_val = "";
		}
		
		fstr->bv_len = ad.bv_len +
			( undef2 ? 1 : 0 ) +
			( f->f_mr_dnattrs ? STRLENOF(":dn") : 0 ) +
			( f->f_mr_rule_text.bv_len ? f->f_mr_rule_text.bv_len + STRLENOF(":") : 0 ) +
			tmp.bv_len + STRLENOF("(:=)");
		fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );

		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s%s:=%s)",
			undef2 ? "?" : "",
			ad.bv_val,
			f->f_mr_dnattrs ? ":dn" : "",
			f->f_mr_rule_text.bv_len ? ":" : "",
			f->f_mr_rule_text.bv_len ? f->f_mr_rule_text.bv_val : "",
			tmp.bv_len ? tmp.bv_val : "" );
		ber_memfree_x( tmp.bv_val, op->o_tmpmemctx );
		} break;

	case SLAPD_FILTER_COMPUTED:
		switch ( f->f_result ) {
		case LDAP_COMPARE_FALSE:
			tmp = ( noundef ? ber_bvF : ber_bvfalse );
			break;

		case LDAP_COMPARE_TRUE:
			tmp = ( noundef ? ber_bvT : ber_bvtrue );
			break;
			
		case SLAPD_COMPARE_UNDEFINED:
			tmp = ber_bvundefined;
			break;
			
		default:
			tmp = ber_bverror;
			break;
		}

		ber_dupbv_x( fstr, &tmp, op->o_tmpmemctx );
		break;
		
	default:
		ber_dupbv_x( fstr, &ber_bvunknown, op->o_tmpmemctx );
		break;
	}
}