/* * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998-1999 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ /* * Copyright (c) 1994 Regents of the University of Michigan. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that this notice is preserved and that due credit is given * to the University of Michigan at Ann Arbor. The name of the University * may not be used to endorse or promote products derived from this * software without specific prior written permission. This software * is provided ``as is'' without express or implied warranty. */ /* * sort.c: LDAP library entry and value sort routines */ #include "ldap-int.h" /* This xp_qsort fixes a memory problem (ABR) on Solaris for the client. * Server is welcome to use it too, but I wasn't sure if it * would be ok to use XP code here. -slamm * * We don't want to require use of libxp when linking with libldap, so * I'll leave use of xp_qsort as a MOZILLA_CLIENT-only thing for now. --mcs */ #if defined(MOZILLA_CLIENT) && defined(SOLARIS) #include "xp_qsort.h" #else #define XP_QSORT qsort #endif typedef struct keycmp { void *kc_arg; LDAP_KEYCMP_CALLBACK *kc_cmp; } keycmp_t; typedef struct keything { keycmp_t *kt_cmp; const struct berval *kt_key; LDAPMessage *kt_msg; } keything_t; static int LDAP_C LDAP_CALLBACK ldapi_keycmp( const void *Lv, const void *Rv ) { auto keything_t **L = (keything_t**)Lv; auto keything_t **R = (keything_t**)Rv; auto keycmp_t *cmp = (*L)->kt_cmp; return cmp->kc_cmp( cmp->kc_arg, (*L)->kt_key, (*R)->kt_key ); } int LDAP_CALL ldap_keysort_entries( LDAP *ld, LDAPMessage **chain, void *arg, LDAP_KEYGEN_CALLBACK *gen, LDAP_KEYCMP_CALLBACK *cmp, LDAP_KEYFREE_CALLBACK *fre) { size_t count, i; keycmp_t kc = {0}; keything_t **kt; LDAPMessage *e, *last; LDAPMessage **ep; if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || chain == NULL || cmp == NULL ) { return( LDAP_PARAM_ERROR ); } count = ldap_count_entries( ld, *chain ); kt = (keything_t**)NSLDAPI_MALLOC( count * (sizeof(keything_t*) + sizeof(keything_t)) ); if ( kt == NULL ) { LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); return( -1 ); } for ( i = 0; i < count; i++ ) { kt[i] = i + (keything_t*)(kt + count); } kc.kc_arg = arg; kc.kc_cmp = cmp; for ( e = *chain, i = 0; i < count; i++, e = e->lm_chain ) { kt[i]->kt_msg = e; kt[i]->kt_cmp = &kc; kt[i]->kt_key = gen( arg, ld, e ); if ( kt[i]->kt_key == NULL ) { if ( fre ) while ( i-- > 0 ) fre( arg, kt[i]->kt_key ); NSLDAPI_FREE( (char*)kt ); LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); return( -1 ); } } last = e; XP_QSORT( (void*)kt, count, (size_t)sizeof(keything_t*), ldapi_keycmp ); ep = chain; for ( i = 0; i < count; i++ ) { *ep = kt[i]->kt_msg; ep = &(*ep)->lm_chain; if ( fre ) fre( arg, kt[i]->kt_key ); } *ep = last; NSLDAPI_FREE( (char*)kt ); return( 0 ); } struct entrything { char **et_vals; LDAPMessage *et_msg; }; typedef int (LDAP_C LDAP_CALLBACK LDAP_CHARCMP_CALLBACK)(char*, char*); typedef int (LDAP_C LDAP_CALLBACK LDAP_VOIDCMP_CALLBACK)(const void*, const void*); static LDAP_CHARCMP_CALLBACK *et_cmp_fn; static LDAP_VOIDCMP_CALLBACK et_cmp; int LDAP_C LDAP_CALLBACK ldap_sort_strcasecmp( const char **a, const char **b ) { /* XXXceb * I am not 100% sure this is the way this should be handled. * For now we will return a 0 on invalid. */ if (NULL == a || NULL == b) return (0); return( strcasecmp( (char *)*a, (char *)*b ) ); } static int LDAP_C LDAP_CALLBACK et_cmp( const void *aa, const void *bb ) { int i, rc; struct entrything *a = (struct entrything *)aa; struct entrything *b = (struct entrything *)bb; if ( a->et_vals == NULL && b->et_vals == NULL ) return( 0 ); if ( a->et_vals == NULL ) return( -1 ); if ( b->et_vals == NULL ) return( 1 ); for ( i = 0; a->et_vals[i] && b->et_vals[i]; i++ ) { if ( (rc = (*et_cmp_fn)( a->et_vals[i], b->et_vals[i] )) != 0 ) { return( rc ); } } if ( a->et_vals[i] == NULL && b->et_vals[i] == NULL ) return( 0 ); if ( a->et_vals[i] == NULL ) return( -1 ); return( 1 ); } int LDAP_CALL ldap_multisort_entries( LDAP *ld, LDAPMessage **chain, char **attr, /* NULL => sort by DN */ LDAP_CMP_CALLBACK *cmp ) { int i, count; struct entrything *et; LDAPMessage *e, *last; LDAPMessage **ep; if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || chain == NULL || cmp == NULL ) { return( LDAP_PARAM_ERROR ); } count = ldap_count_entries( ld, *chain ); if ( (et = (struct entrything *)NSLDAPI_MALLOC( count * sizeof(struct entrything) )) == NULL ) { LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); return( -1 ); } e = *chain; for ( i = 0; i < count; i++ ) { et[i].et_msg = e; et[i].et_vals = NULL; if ( attr == NULL ) { char *dn; dn = ldap_get_dn( ld, e ); et[i].et_vals = ldap_explode_dn( dn, 1 ); NSLDAPI_FREE( dn ); } else { int attrcnt; char **vals; for ( attrcnt = 0; attr[attrcnt] != NULL; attrcnt++ ) { vals = ldap_get_values( ld, e, attr[attrcnt] ); if ( ldap_charray_merge( &(et[i].et_vals), vals ) != 0 ) { int j; /* XXX risky: ldap_value_free( vals ); */ for ( j = 0; j <= i; j++ ) ldap_value_free( et[j].et_vals ); NSLDAPI_FREE( (char *) et ); LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); return( -1 ); } if ( vals != NULL ) { NSLDAPI_FREE( (char *)vals ); } } } e = e->lm_chain; } last = e; et_cmp_fn = (LDAP_CHARCMP_CALLBACK *)cmp; XP_QSORT( (void *) et, (size_t) count, (size_t) sizeof(struct entrything), et_cmp ); ep = chain; for ( i = 0; i < count; i++ ) { *ep = et[i].et_msg; ep = &(*ep)->lm_chain; ldap_value_free( et[i].et_vals ); } *ep = last; NSLDAPI_FREE( (char *) et ); return( 0 ); } int LDAP_CALL ldap_sort_entries( LDAP *ld, LDAPMessage **chain, char *attr, /* NULL => sort by DN */ LDAP_CMP_CALLBACK *cmp ) { char *attrs[2]; attrs[0] = attr; attrs[1] = NULL; return( ldap_multisort_entries( ld, chain, attr ? attrs : NULL, cmp ) ); } int LDAP_CALL ldap_sort_values( LDAP *ld, char **vals, LDAP_VALCMP_CALLBACK *cmp ) { int nel; if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || cmp == NULL ) { return( LDAP_PARAM_ERROR ); } if ( NULL == vals) { LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL ); return( LDAP_PARAM_ERROR ); } for ( nel = 0; vals[nel] != NULL; nel++ ) ; /* NULL */ XP_QSORT( vals, nel, sizeof(char *), (LDAP_VOIDCMP_CALLBACK *)cmp ); return( LDAP_SUCCESS ); }