+ return rtn;
+ }
+ }
+
+ if( ntags > 0 ) {
+ int i;
+
+ if( tagslen > MAX_TAGS_LEN ) {
+ *text = "tagging options too long";
+ return rtn;
+ }
+
+ desc.ad_tags.bv_val = tagbuf;
+ tagslen = 0;
+
+ for( i=0; i<ntags; i++ ) {
+ AC_MEMCPY( &desc.ad_tags.bv_val[tagslen],
+ tags[i].bv_val, tags[i].bv_len );
+
+ tagslen += tags[i].bv_len;
+ desc.ad_tags.bv_val[tagslen++] = ';';
+ }
+
+ desc.ad_tags.bv_val[--tagslen] = '\0';
+ desc.ad_tags.bv_len = tagslen;
+ }
+
+ /* see if a matching description is already cached */
+ for (d2 = desc.ad_type->sat_ad; d2; d2=d2->ad_next) {
+ if( d2->ad_flags != desc.ad_flags ) {
+ continue;
+ }
+ if( d2->ad_tags.bv_len != desc.ad_tags.bv_len ) {
+ continue;
+ }
+ if( d2->ad_tags.bv_len == 0 ) {
+ break;
+ }
+ if( strncasecmp( d2->ad_tags.bv_val, desc.ad_tags.bv_val,
+ desc.ad_tags.bv_len ) == 0 )
+ {
+ break;
+ }
+ }
+
+ /* Not found, add new one */
+ while (d2 == NULL) {
+ size_t dlen = 0;
+ ldap_pvt_thread_mutex_lock( &desc.ad_type->sat_ad_mutex );
+ /* check again now that we've locked */
+ for (d2 = desc.ad_type->sat_ad; d2; d2=d2->ad_next) {
+ if (d2->ad_flags != desc.ad_flags)
+ continue;
+ if (d2->ad_tags.bv_len != desc.ad_tags.bv_len)
+ continue;
+ if (d2->ad_tags.bv_len == 0)
+ break;
+ if (strncasecmp(d2->ad_tags.bv_val, desc.ad_tags.bv_val,
+ desc.ad_tags.bv_len) == 0)
+ break;
+ }
+ if (d2) {
+ ldap_pvt_thread_mutex_unlock( &desc.ad_type->sat_ad_mutex );
+ break;
+ }
+
+ /* Allocate a single contiguous block. If there are no
+ * options, we just need space for the AttrDesc structure.
+ * Otherwise, we need to tack on the full name length +
+ * options length, + maybe tagging options length again.
+ */
+ if (desc.ad_tags.bv_len || desc.ad_flags != SLAP_DESC_NONE) {
+ dlen = desc.ad_type->sat_cname.bv_len + 1;
+ if (desc.ad_tags.bv_len) {
+ dlen += 1+desc.ad_tags.bv_len;
+ }
+ if( slap_ad_is_binary( &desc ) ) {
+ dlen += sizeof(";binary")+desc.ad_tags.bv_len;
+ }
+ }
+
+ d2 = ch_malloc(sizeof(AttributeDescription) + dlen);
+ d2->ad_next = NULL;
+ d2->ad_type = desc.ad_type;
+ d2->ad_flags = desc.ad_flags;
+ d2->ad_cname.bv_len = desc.ad_type->sat_cname.bv_len;
+ d2->ad_tags.bv_len = desc.ad_tags.bv_len;
+
+ if (dlen == 0) {
+ d2->ad_cname.bv_val = d2->ad_type->sat_cname.bv_val;
+ d2->ad_tags.bv_val = NULL;
+ } else {
+ char *cp, *op, *lp;
+ int j;
+ d2->ad_cname.bv_val = (char *)(d2+1);
+ strcpy(d2->ad_cname.bv_val, d2->ad_type->sat_cname.bv_val);
+ cp = d2->ad_cname.bv_val + d2->ad_cname.bv_len;
+ if( slap_ad_is_binary( &desc ) ) {
+ op = cp;
+ lp = NULL;
+ if( desc.ad_tags.bv_len ) {
+ lp = desc.ad_tags.bv_val;
+ while( strncasecmp(lp, "binary", sizeof("binary")-1) < 0
+ && (lp = strchr( lp, ';' )) != NULL )
+ ++lp;
+ if( lp != desc.ad_tags.bv_val ) {
+ *cp++ = ';';
+ j = (lp
+ ? lp - desc.ad_tags.bv_val - 1
+ : strlen( desc.ad_tags.bv_val ));
+ cp = lutil_strncopy(cp, desc.ad_tags.bv_val, j);
+ }
+ }
+ cp = lutil_strcopy(cp, ";binary");
+ if( lp != NULL ) {
+ *cp++ = ';';
+ cp = lutil_strcopy(cp, lp);
+ }
+ d2->ad_cname.bv_len = cp - d2->ad_cname.bv_val;
+ if( desc.ad_tags.bv_len )
+ ldap_pvt_str2lower(op);
+ j = 1;
+ } else {
+ j = 0;
+ }
+ if( desc.ad_tags.bv_len ) {
+ lp = d2->ad_cname.bv_val + d2->ad_cname.bv_len + j;
+ if ( j == 0 )
+ *lp++ = ';';
+ d2->ad_tags.bv_val = lp;
+ strcpy(lp, desc.ad_tags.bv_val);
+ ldap_pvt_str2lower(lp);
+ if( j == 0 )
+ d2->ad_cname.bv_len += 1 + desc.ad_tags.bv_len;
+ }
+ }
+ /* Add new desc to list. We always want the bare Desc with
+ * no options to stay at the head of the list, assuming
+ * that one will be used most frequently.
+ */
+ if (desc.ad_type->sat_ad == NULL || dlen == 0) {
+ d2->ad_next = desc.ad_type->sat_ad;
+ desc.ad_type->sat_ad = d2;
+ } else {
+ d2->ad_next = desc.ad_type->sat_ad->ad_next;
+ desc.ad_type->sat_ad->ad_next = d2;
+ }
+ ldap_pvt_thread_mutex_unlock( &desc.ad_type->sat_ad_mutex );
+ }
+
+ if( *ad == NULL ) {
+ *ad = d2;
+ } else {
+ **ad = *d2;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int is_ad_subtags(
+ struct berval *subtagsbv,
+ struct berval *suptagsbv )
+{
+ const char *suptags, *supp, *supdelimp;
+ const char *subtags, *subp, *subdelimp;
+ int suplen, sublen;
+
+ subtags =subtagsbv->bv_val;
+ suptags =suptagsbv->bv_val;
+
+ for( supp=suptags ; supp; supp=supdelimp ) {
+ supdelimp = strchrlen( supp, ';', &suplen );
+ if( supdelimp ) supdelimp++;
+
+ for( subp=subtags ; subp; subp=subdelimp ) {
+ subdelimp = strchrlen( subp, ';', &sublen );
+ if( subdelimp ) subdelimp++;
+
+ if ( suplen > sublen
+ ? ( suplen-1 == sublen && supp[suplen-1] == '-'
+ && strncmp( supp, subp, sublen ) == 0 )
+ : ( ( suplen == sublen || supp[suplen-1] == '-' )
+ && strncmp( supp, subp, suplen ) == 0 ) )
+ {
+ goto match;
+ }