+int
+modify_add_values(
+ Entry *e,
+ Modification *mod,
+ int permissive,
+ const char **text,
+ char *textbuf,
+ size_t textlen )
+{
+ int rc;
+ const char *op;
+ Attribute *a;
+ Modification pmod = *mod;
+
+ switch ( mod->sm_op ) {
+ case LDAP_MOD_ADD:
+ op = "add";
+ break;
+ case LDAP_MOD_REPLACE:
+ op = "replace";
+ break;
+ default:
+ op = "?";
+ assert( 0 );
+ }
+
+ /* check if values to add exist in attribute */
+ a = attr_find( e->e_attrs, mod->sm_desc );
+ if ( a != NULL ) {
+ int rc, i, j, p;
+ MatchingRule *mr;
+
+ mr = mod->sm_desc->ad_type->sat_equality;
+ if( mr == NULL || !mr->smr_match ) {
+ /* do not allow add of additional attribute
+ if no equality rule exists */
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/%s: %s: no equality matching rule",
+ op, mod->sm_desc->ad_cname.bv_val );
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+
+ if ( permissive ) {
+ for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ) {
+ /* EMPTY -- just counting 'em */;
+ }
+
+ pmod.sm_values = (BerVarray)ch_malloc(
+ (i + 1) * sizeof( struct berval ));
+ if ( pmod.sm_nvalues != NULL ) {
+ pmod.sm_nvalues = (BerVarray)ch_malloc(
+ (i + 1) * sizeof( struct berval ));
+ }
+ }
+
+ /* no normalization is done in this routine nor
+ * in the matching routines called by this routine.
+ * values are now normalized once on input to the
+ * server (whether from LDAP or from the underlying
+ * database).
+ */
+ for ( p = i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ) {
+ int match;
+
+ assert( a->a_vals[0].bv_val != NULL );
+ for ( j = 0; !BER_BVISNULL( &a->a_vals[j] ); j++ ) {
+ if ( mod->sm_nvalues ) {
+ rc = ordered_value_match( &match, mod->sm_desc, mr,
+ SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX
+ | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
+ | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
+ &a->a_nvals[j], &mod->sm_nvalues[i], text );
+ } else {
+ rc = ordered_value_match( &match, mod->sm_desc, mr,
+ SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
+ &a->a_vals[j], &mod->sm_values[i], text );
+ }
+
+ if ( rc == LDAP_SUCCESS && match == 0 ) {
+ /* value already exists */
+ if ( permissive ) break;
+
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/%s: %s: value #%d already exists",
+ op, mod->sm_desc->ad_cname.bv_val, i );
+ return LDAP_TYPE_OR_VALUE_EXISTS;
+
+ } else if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+ }
+
+ if ( permissive && match != 0 ) {
+ if ( pmod.sm_nvalues ) {
+ pmod.sm_nvalues[p] = mod->sm_nvalues[i];
+ }
+ pmod.sm_values[p++] = mod->sm_values[i];
+ }
+ }
+
+ if ( permissive ) {
+ if ( p == 0 ) {
+ /* all new values match exist */
+ ch_free( pmod.sm_values );
+ if ( pmod.sm_nvalues ) ch_free( pmod.sm_nvalues );
+ return LDAP_SUCCESS;
+ }
+
+ BER_BVZERO( &pmod.sm_values[p] );
+ if ( pmod.sm_nvalues ) {
+ BER_BVZERO( &pmod.sm_nvalues[p] );
+ }
+ }
+ }
+
+ /* no - add them */
+ if ( mod->sm_desc->ad_type->sat_flags & SLAP_AT_ORDERED_VAL ) {
+ rc = ordered_value_add( e, mod->sm_desc, a,
+ pmod.sm_values, pmod.sm_nvalues );
+ } else {
+ rc = attr_merge( e, mod->sm_desc, pmod.sm_values, pmod.sm_nvalues );
+ }
+
+ if ( a != NULL && permissive ) {
+ ch_free( pmod.sm_values );
+ if ( pmod.sm_nvalues ) ch_free( pmod.sm_nvalues );
+ }
+
+ if ( rc != 0 ) {
+ /* this should return result of attr_merge */
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/%s: %s: merge error",
+ op, mod->sm_desc->ad_cname.bv_val );
+ return LDAP_OTHER;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+modify_delete_values(
+ Entry *e,
+ Modification *m,
+ int perm,
+ const char **text,
+ char *textbuf, size_t textlen )
+{
+ return modify_delete_vindex( e, m, perm, text, textbuf, textlen, NULL );
+}
+
+int
+modify_delete_vindex(
+ Entry *e,