1/* $OpenLDAP$ */ 2/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 3 * 4 * Copyright 1998-2011 The OpenLDAP Foundation. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted only as authorized by the OpenLDAP 9 * Public License. 10 * 11 * A copy of this license is available in the file LICENSE in the 12 * top-level directory of the distribution or, alternatively, at 13 * <http://www.OpenLDAP.org/license.html>. 14 */ 15 16#include "portable.h" 17 18#include <ac/stdlib.h> 19#include <ac/string.h> 20 21#include "lber-int.h" 22 23#ifdef LDAP_MEMORY_TRACE 24#include <stdio.h> 25#endif 26 27#ifdef LDAP_MEMORY_DEBUG 28/* 29 * LDAP_MEMORY_DEBUG should only be enabled for the purposes of 30 * debugging memory management within OpenLDAP libraries and slapd. 31 * 32 * It should only be enabled by an experienced developer as it causes 33 * the inclusion of numerous assert()'s, many of which may be triggered 34 * by a prefectly valid program. If LDAP_MEMORY_DEBUG & 2 is true, 35 * that includes asserts known to break both slapd and current clients. 36 * 37 * The code behind this macro is subject to change as needed to 38 * support this testing. 39 */ 40 41struct ber_mem_hdr { 42 ber_int_t bm_top; /* Pattern to detect buf overrun from prev buffer */ 43 ber_int_t bm_length; /* Length of user allocated area */ 44#ifdef LDAP_MEMORY_TRACE 45 ber_int_t bm_sequence; /* Allocation sequence number */ 46#endif 47 union bmu_align_u { /* Force alignment, pattern to detect back clobber */ 48 ber_len_t bmu_len_t; 49 ber_tag_t bmu_tag_t; 50 ber_int_t bmu_int_t; 51 52 size_t bmu_size_t; 53 void * bmu_voidp; 54 double bmu_double; 55 long bmu_long; 56 long (*bmu_funcp)( double ); 57 unsigned char bmu_char[4]; 58 } ber_align; 59#define bm_junk ber_align.bmu_len_t 60#define bm_data ber_align.bmu_char[1] 61#define bm_char ber_align.bmu_char 62}; 63 64/* Pattern at top of allocated space */ 65#define LBER_MEM_JUNK ((ber_int_t) 0xdeaddada) 66 67static const struct ber_mem_hdr ber_int_mem_hdr = { LBER_MEM_JUNK }; 68 69/* Note sequence and ber_int_meminuse are counters, but are not 70 * thread safe. If you want to use these values for multithreaded applications, 71 * you must put mutexes around them, otherwise they will have incorrect values. 72 * When debugging, if you sort the debug output, the sequence number will 73 * put allocations/frees together. It is then a simple matter to write a script 74 * to find any allocations that don't have a buffer free function. 75 */ 76long ber_int_meminuse = 0; 77#ifdef LDAP_MEMORY_TRACE 78static ber_int_t sequence = 0; 79#endif 80 81/* Pattern placed just before user data */ 82static unsigned char toppattern[4] = { 0xde, 0xad, 0xba, 0xde }; 83/* Pattern placed just after user data */ 84static unsigned char endpattern[4] = { 0xd1, 0xed, 0xde, 0xca }; 85 86#define mbu_len sizeof(ber_int_mem_hdr.ber_align) 87 88/* Test if pattern placed just before user data is good */ 89#define testdatatop(val) ( \ 90 *(val->bm_char+mbu_len-4)==toppattern[0] && \ 91 *(val->bm_char+mbu_len-3)==toppattern[1] && \ 92 *(val->bm_char+mbu_len-2)==toppattern[2] && \ 93 *(val->bm_char+mbu_len-1)==toppattern[3] ) 94 95/* Place pattern just before user data */ 96#define setdatatop(val) *(val->bm_char+mbu_len-4)=toppattern[0]; \ 97 *(val->bm_char+mbu_len-3)=toppattern[1]; \ 98 *(val->bm_char+mbu_len-2)=toppattern[2]; \ 99 *(val->bm_char+mbu_len-1)=toppattern[3]; 100 101/* Test if pattern placed just after user data is good */ 102#define testend(val) ( *((unsigned char *)val+0)==endpattern[0] && \ 103 *((unsigned char *)val+1)==endpattern[1] && \ 104 *((unsigned char *)val+2)==endpattern[2] && \ 105 *((unsigned char *)val+3)==endpattern[3] ) 106 107/* Place pattern just after user data */ 108#define setend(val) *((unsigned char *)val+0)=endpattern[0]; \ 109 *((unsigned char *)val+1)=endpattern[1]; \ 110 *((unsigned char *)val+2)=endpattern[2]; \ 111 *((unsigned char *)val+3)=endpattern[3]; 112 113#define BER_MEM_BADADDR ((void *) &ber_int_mem_hdr.bm_data) 114#define BER_MEM_VALID(p) do { \ 115 assert( (p) != BER_MEM_BADADDR ); \ 116 assert( (p) != (void *) &ber_int_mem_hdr ); \ 117 } while(0) 118 119#else 120#define BER_MEM_VALID(p) /* no-op */ 121#endif 122 123BerMemoryFunctions *ber_int_memory_fns = NULL; 124 125void 126ber_memfree_x( void *p, void *ctx ) 127{ 128 if( p == NULL ) { 129 return; 130 } 131 132 BER_MEM_VALID( p ); 133 134 if( ber_int_memory_fns == NULL || ctx == NULL ) { 135#ifdef LDAP_MEMORY_DEBUG 136 struct ber_mem_hdr *mh = (struct ber_mem_hdr *) 137 ((char *)p - sizeof(struct ber_mem_hdr)); 138 assert( mh->bm_top == LBER_MEM_JUNK); 139 assert( testdatatop( mh)); 140 assert( testend( (char *)&mh[1] + mh->bm_length) ); 141 ber_int_meminuse -= mh->bm_length; 142 143#ifdef LDAP_MEMORY_TRACE 144 fprintf(stderr, "0x%08lx 0x%08lx -f- %ld ber_memfree %ld\n", 145 (long)mh->bm_sequence, (long)mh, (long)mh->bm_length, 146 ber_int_meminuse); 147#endif 148 /* Fill the free space with poison */ 149 memset( mh, 0xff, mh->bm_length + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t)); 150 free( mh ); 151#else 152 free( p ); 153#endif 154 return; 155 } 156 157 assert( ber_int_memory_fns->bmf_free != 0 ); 158 159 (*ber_int_memory_fns->bmf_free)( p, ctx ); 160} 161 162void 163ber_memfree( void *p ) 164{ 165 ber_memfree_x(p, NULL); 166} 167 168void 169ber_memvfree_x( void **vec, void *ctx ) 170{ 171 int i; 172 173 if( vec == NULL ) { 174 return; 175 } 176 177 BER_MEM_VALID( vec ); 178 179 for ( i = 0; vec[i] != NULL; i++ ) { 180 ber_memfree_x( vec[i], ctx ); 181 } 182 183 ber_memfree_x( vec, ctx ); 184} 185 186void 187ber_memvfree( void **vec ) 188{ 189 ber_memvfree_x( vec, NULL ); 190} 191 192void * 193ber_memalloc_x( ber_len_t s, void *ctx ) 194{ 195 void *new; 196 197 if( s == 0 ) { 198 LDAP_MEMORY_DEBUG_ASSERT( s != 0 ); 199 return NULL; 200 } 201 202 if( ber_int_memory_fns == NULL || ctx == NULL ) { 203#ifdef LDAP_MEMORY_DEBUG 204 new = malloc(s + sizeof(struct ber_mem_hdr) + sizeof( ber_int_t)); 205 if( new ) 206 { 207 struct ber_mem_hdr *mh = new; 208 mh->bm_top = LBER_MEM_JUNK; 209 mh->bm_length = s; 210 setdatatop( mh); 211 setend( (char *)&mh[1] + mh->bm_length ); 212 213 ber_int_meminuse += mh->bm_length; /* Count mem inuse */ 214 215#ifdef LDAP_MEMORY_TRACE 216 mh->bm_sequence = sequence++; 217 fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memalloc %ld\n", 218 (long)mh->bm_sequence, (long)mh, (long)mh->bm_length, 219 ber_int_meminuse); 220#endif 221 /* poison new memory */ 222 memset( (char *)&mh[1], 0xff, s); 223 224 BER_MEM_VALID( &mh[1] ); 225 new = &mh[1]; 226 } 227#else 228 new = malloc( s ); 229#endif 230 } else { 231 new = (*ber_int_memory_fns->bmf_malloc)( s, ctx ); 232 } 233 234 if( new == NULL ) { 235 ber_errno = LBER_ERROR_MEMORY; 236 } 237 238 return new; 239} 240 241void * 242ber_memalloc( ber_len_t s ) 243{ 244 return ber_memalloc_x( s, NULL ); 245} 246 247void * 248ber_memcalloc_x( ber_len_t n, ber_len_t s, void *ctx ) 249{ 250 void *new; 251 252 if( n == 0 || s == 0 ) { 253 LDAP_MEMORY_DEBUG_ASSERT( n != 0 && s != 0); 254 return NULL; 255 } 256 257 if( ber_int_memory_fns == NULL || ctx == NULL ) { 258#ifdef LDAP_MEMORY_DEBUG 259 new = n < (-sizeof(struct ber_mem_hdr) - sizeof(ber_int_t)) / s 260 ? calloc(1, n*s + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t)) 261 : NULL; 262 if( new ) 263 { 264 struct ber_mem_hdr *mh = new; 265 266 mh->bm_top = LBER_MEM_JUNK; 267 mh->bm_length = n*s; 268 setdatatop( mh); 269 setend( (char *)&mh[1] + mh->bm_length ); 270 271 ber_int_meminuse += mh->bm_length; 272 273#ifdef LDAP_MEMORY_TRACE 274 mh->bm_sequence = sequence++; 275 fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memcalloc %ld\n", 276 (long)mh->bm_sequence, (long)mh, (long)mh->bm_length, 277 ber_int_meminuse); 278#endif 279 BER_MEM_VALID( &mh[1] ); 280 new = &mh[1]; 281 } 282#else 283 new = calloc( n, s ); 284#endif 285 286 } else { 287 new = (*ber_int_memory_fns->bmf_calloc)( n, s, ctx ); 288 } 289 290 if( new == NULL ) { 291 ber_errno = LBER_ERROR_MEMORY; 292 } 293 294 return new; 295} 296 297void * 298ber_memcalloc( ber_len_t n, ber_len_t s ) 299{ 300 return ber_memcalloc_x( n, s, NULL ); 301} 302 303void * 304ber_memrealloc_x( void* p, ber_len_t s, void *ctx ) 305{ 306 void *new = NULL; 307 308 /* realloc(NULL,s) -> malloc(s) */ 309 if( p == NULL ) { 310 return ber_memalloc_x( s, ctx ); 311 } 312 313 /* realloc(p,0) -> free(p) */ 314 if( s == 0 ) { 315 ber_memfree_x( p, ctx ); 316 return NULL; 317 } 318 319 BER_MEM_VALID( p ); 320 321 if( ber_int_memory_fns == NULL || ctx == NULL ) { 322#ifdef LDAP_MEMORY_DEBUG 323 ber_int_t oldlen; 324 struct ber_mem_hdr *mh = (struct ber_mem_hdr *) 325 ((char *)p - sizeof(struct ber_mem_hdr)); 326 assert( mh->bm_top == LBER_MEM_JUNK); 327 assert( testdatatop( mh)); 328 assert( testend( (char *)&mh[1] + mh->bm_length) ); 329 oldlen = mh->bm_length; 330 331 p = realloc( mh, s + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t) ); 332 if( p == NULL ) { 333 ber_errno = LBER_ERROR_MEMORY; 334 return NULL; 335 } 336 337 mh = p; 338 mh->bm_length = s; 339 setend( (char *)&mh[1] + mh->bm_length ); 340 if( s > oldlen ) { 341 /* poison any new memory */ 342 memset( (char *)&mh[1] + oldlen, 0xff, s - oldlen); 343 } 344 345 assert( mh->bm_top == LBER_MEM_JUNK); 346 assert( testdatatop( mh)); 347 348 ber_int_meminuse += s - oldlen; 349#ifdef LDAP_MEMORY_TRACE 350 fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memrealloc %ld\n", 351 (long)mh->bm_sequence, (long)mh, (long)mh->bm_length, 352 ber_int_meminuse); 353#endif 354 BER_MEM_VALID( &mh[1] ); 355 return &mh[1]; 356#else 357 new = realloc( p, s ); 358#endif 359 } else { 360 new = (*ber_int_memory_fns->bmf_realloc)( p, s, ctx ); 361 } 362 363 if( new == NULL ) { 364 ber_errno = LBER_ERROR_MEMORY; 365 } 366 367 return new; 368} 369 370void * 371ber_memrealloc( void* p, ber_len_t s ) 372{ 373 return ber_memrealloc_x( p, s, NULL ); 374} 375 376void 377ber_bvfree_x( struct berval *bv, void *ctx ) 378{ 379 if( bv == NULL ) { 380 return; 381 } 382 383 BER_MEM_VALID( bv ); 384 385 if ( bv->bv_val != NULL ) { 386 ber_memfree_x( bv->bv_val, ctx ); 387 } 388 389 ber_memfree_x( (char *) bv, ctx ); 390} 391 392void 393ber_bvfree( struct berval *bv ) 394{ 395 ber_bvfree_x( bv, NULL ); 396} 397 398void 399ber_bvecfree_x( struct berval **bv, void *ctx ) 400{ 401 int i; 402 403 if( bv == NULL ) { 404 return; 405 } 406 407 BER_MEM_VALID( bv ); 408 409 /* count elements */ 410 for ( i = 0; bv[i] != NULL; i++ ) ; 411 412 /* free in reverse order */ 413 for ( i--; i >= 0; i-- ) { 414 ber_bvfree_x( bv[i], ctx ); 415 } 416 417 ber_memfree_x( (char *) bv, ctx ); 418} 419 420void 421ber_bvecfree( struct berval **bv ) 422{ 423 ber_bvecfree_x( bv, NULL ); 424} 425 426int 427ber_bvecadd_x( struct berval ***bvec, struct berval *bv, void *ctx ) 428{ 429 ber_len_t i; 430 struct berval **new; 431 432 if( *bvec == NULL ) { 433 if( bv == NULL ) { 434 /* nothing to add */ 435 return 0; 436 } 437 438 *bvec = ber_memalloc_x( 2 * sizeof(struct berval *), ctx ); 439 440 if( *bvec == NULL ) { 441 return -1; 442 } 443 444 (*bvec)[0] = bv; 445 (*bvec)[1] = NULL; 446 447 return 1; 448 } 449 450 BER_MEM_VALID( bvec ); 451 452 /* count entries */ 453 for ( i = 0; (*bvec)[i] != NULL; i++ ) { 454 /* EMPTY */; 455 } 456 457 if( bv == NULL ) { 458 return i; 459 } 460 461 new = ber_memrealloc_x( *bvec, (i+2) * sizeof(struct berval *), ctx); 462 463 if( new == NULL ) { 464 return -1; 465 } 466 467 *bvec = new; 468 469 (*bvec)[i++] = bv; 470 (*bvec)[i] = NULL; 471 472 return i; 473} 474 475int 476ber_bvecadd( struct berval ***bvec, struct berval *bv ) 477{ 478 return ber_bvecadd_x( bvec, bv, NULL ); 479} 480 481struct berval * 482ber_dupbv_x( 483 struct berval *dst, struct berval *src, void *ctx ) 484{ 485 struct berval *new; 486 487 if( src == NULL ) { 488 ber_errno = LBER_ERROR_PARAM; 489 return NULL; 490 } 491 492 if ( dst ) { 493 new = dst; 494 } else { 495 if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) { 496 return NULL; 497 } 498 } 499 500 if ( src->bv_val == NULL ) { 501 new->bv_val = NULL; 502 new->bv_len = 0; 503 return new; 504 } 505 506 if(( new->bv_val = ber_memalloc_x( src->bv_len + 1, ctx )) == NULL ) { 507 if ( !dst ) 508 ber_memfree_x( new, ctx ); 509 return NULL; 510 } 511 512 AC_MEMCPY( new->bv_val, src->bv_val, src->bv_len ); 513 new->bv_val[src->bv_len] = '\0'; 514 new->bv_len = src->bv_len; 515 516 return new; 517} 518 519struct berval * 520ber_dupbv( 521 struct berval *dst, struct berval *src ) 522{ 523 return ber_dupbv_x( dst, src, NULL ); 524} 525 526struct berval * 527ber_bvdup( 528 struct berval *src ) 529{ 530 return ber_dupbv_x( NULL, src, NULL ); 531} 532 533struct berval * 534ber_str2bv_x( 535 LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv, 536 void *ctx) 537{ 538 struct berval *new; 539 540 if( s == NULL ) { 541 ber_errno = LBER_ERROR_PARAM; 542 return NULL; 543 } 544 545 if( bv ) { 546 new = bv; 547 } else { 548 if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) { 549 return NULL; 550 } 551 } 552 553 new->bv_len = len ? len : strlen( s ); 554 if ( dup ) { 555 if ( (new->bv_val = ber_memalloc_x( new->bv_len+1, ctx )) == NULL ) { 556 if ( !bv ) 557 ber_memfree_x( new, ctx ); 558 return NULL; 559 } 560 561 AC_MEMCPY( new->bv_val, s, new->bv_len ); 562 new->bv_val[new->bv_len] = '\0'; 563 } else { 564 new->bv_val = (char *) s; 565 } 566 567 return( new ); 568} 569 570struct berval * 571ber_str2bv( 572 LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv) 573{ 574 return ber_str2bv_x( s, len, dup, bv, NULL ); 575} 576 577struct berval * 578ber_mem2bv_x( 579 LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv, 580 void *ctx) 581{ 582 struct berval *new; 583 584 if( s == NULL ) { 585 ber_errno = LBER_ERROR_PARAM; 586 return NULL; 587 } 588 589 if( bv ) { 590 new = bv; 591 } else { 592 if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) { 593 return NULL; 594 } 595 } 596 597 new->bv_len = len; 598 if ( dup ) { 599 if ( (new->bv_val = ber_memalloc_x( new->bv_len+1, ctx )) == NULL ) { 600 if ( !bv ) { 601 ber_memfree_x( new, ctx ); 602 } 603 return NULL; 604 } 605 606 AC_MEMCPY( new->bv_val, s, new->bv_len ); 607 new->bv_val[new->bv_len] = '\0'; 608 } else { 609 new->bv_val = (char *) s; 610 } 611 612 return( new ); 613} 614 615struct berval * 616ber_mem2bv( 617 LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv) 618{ 619 return ber_mem2bv_x( s, len, dup, bv, NULL ); 620} 621 622char * 623ber_strdup_x( LDAP_CONST char *s, void *ctx ) 624{ 625 char *p; 626 size_t len; 627 628#ifdef LDAP_MEMORY_DEBUG 629 assert(s != NULL); /* bv damn better point to something */ 630#endif 631 632 if( s == NULL ) { 633 ber_errno = LBER_ERROR_PARAM; 634 return NULL; 635 } 636 637 len = strlen( s ) + 1; 638 if ( (p = ber_memalloc_x( len, ctx )) != NULL ) { 639 AC_MEMCPY( p, s, len ); 640 } 641 642 return p; 643} 644 645char * 646ber_strdup( LDAP_CONST char *s ) 647{ 648 return ber_strdup_x( s, NULL ); 649} 650 651ber_len_t 652ber_strnlen( LDAP_CONST char *s, ber_len_t len ) 653{ 654 ber_len_t l; 655 656 for ( l = 0; l < len && s[l] != '\0'; l++ ) ; 657 658 return l; 659} 660 661char * 662ber_strndup_x( LDAP_CONST char *s, ber_len_t l, void *ctx ) 663{ 664 char *p; 665 size_t len; 666 667#ifdef LDAP_MEMORY_DEBUG 668 assert(s != NULL); /* bv damn better point to something */ 669#endif 670 671 if( s == NULL ) { 672 ber_errno = LBER_ERROR_PARAM; 673 return NULL; 674 } 675 676 len = ber_strnlen( s, l ); 677 if ( (p = ber_memalloc_x( len + 1, ctx )) != NULL ) { 678 AC_MEMCPY( p, s, len ); 679 p[len] = '\0'; 680 } 681 682 return p; 683} 684 685char * 686ber_strndup( LDAP_CONST char *s, ber_len_t l ) 687{ 688 return ber_strndup_x( s, l, NULL ); 689} 690 691/* 692 * dst is resized as required by src and the value of src is copied into dst 693 * dst->bv_val must be NULL (and dst->bv_len must be 0), or it must be 694 * alloc'ed with the context ctx 695 */ 696struct berval * 697ber_bvreplace_x( struct berval *dst, LDAP_CONST struct berval *src, void *ctx ) 698{ 699 assert( dst != NULL ); 700 assert( !BER_BVISNULL( src ) ); 701 702 if ( BER_BVISNULL( dst ) || dst->bv_len < src->bv_len ) { 703 dst->bv_val = ber_memrealloc_x( dst->bv_val, src->bv_len + 1, ctx ); 704 } 705 706 AC_MEMCPY( dst->bv_val, src->bv_val, src->bv_len + 1 ); 707 dst->bv_len = src->bv_len; 708 709 return dst; 710} 711 712struct berval * 713ber_bvreplace( struct berval *dst, LDAP_CONST struct berval *src ) 714{ 715 return ber_bvreplace_x( dst, src, NULL ); 716} 717 718void 719ber_bvarray_free_x( BerVarray a, void *ctx ) 720{ 721 int i; 722 723 if (a) { 724 BER_MEM_VALID( a ); 725 726 /* count elements */ 727 for (i=0; a[i].bv_val; i++) ; 728 729 /* free in reverse order */ 730 for (i--; i>=0; i--) { 731 ber_memfree_x(a[i].bv_val, ctx); 732 } 733 734 ber_memfree_x(a, ctx); 735 } 736} 737 738void 739ber_bvarray_free( BerVarray a ) 740{ 741 ber_bvarray_free_x(a, NULL); 742} 743 744int 745ber_bvarray_dup_x( BerVarray *dst, BerVarray src, void *ctx ) 746{ 747 int i, j; 748 BerVarray new; 749 750 if ( !src ) { 751 *dst = NULL; 752 return 0; 753 } 754 755 for (i=0; !BER_BVISNULL( &src[i] ); i++) ; 756 new = ber_memalloc_x(( i+1 ) * sizeof(BerValue), ctx ); 757 if ( !new ) 758 return -1; 759 for (j=0; j<i; j++) { 760 ber_dupbv_x( &new[j], &src[j], ctx ); 761 if ( BER_BVISNULL( &new[j] )) { 762 ber_bvarray_free_x( new, ctx ); 763 return -1; 764 } 765 } 766 BER_BVZERO( &new[j] ); 767 *dst = new; 768 return 0; 769} 770 771int 772ber_bvarray_add_x( BerVarray *a, BerValue *bv, void *ctx ) 773{ 774 int n; 775 776 if ( *a == NULL ) { 777 if (bv == NULL) { 778 return 0; 779 } 780 n = 0; 781 782 *a = (BerValue *) ber_memalloc_x( 2 * sizeof(BerValue), ctx ); 783 if ( *a == NULL ) { 784 return -1; 785 } 786 787 } else { 788 BerVarray atmp; 789 BER_MEM_VALID( a ); 790 791 for ( n = 0; *a != NULL && (*a)[n].bv_val != NULL; n++ ) { 792 ; /* just count them */ 793 } 794 795 if (bv == NULL) { 796 return n; 797 } 798 799 atmp = (BerValue *) ber_memrealloc_x( (char *) *a, 800 (n + 2) * sizeof(BerValue), ctx ); 801 802 if( atmp == NULL ) { 803 return -1; 804 } 805 806 *a = atmp; 807 } 808 809 (*a)[n++] = *bv; 810 (*a)[n].bv_val = NULL; 811 (*a)[n].bv_len = 0; 812 813 return n; 814} 815 816int 817ber_bvarray_add( BerVarray *a, BerValue *bv ) 818{ 819 return ber_bvarray_add_x( a, bv, NULL ); 820} 821