1 /* 2 * Sorted array routines for CUPS. 3 * 4 * Copyright 2007-2014 by Apple Inc. 5 * Copyright 1997-2007 by Easy Software Products. 6 * 7 * These coded instructions, statements, and computer programs are the 8 * property of Apple Inc. and are protected by Federal copyright 9 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 10 * which should have been included with this file. If this file is 11 * missing or damaged, see the license at "http://www.cups.org/". 12 * 13 * This file is subject to the Apple OS-Developed Software exception. 14 */ 15 16 /* 17 * Include necessary headers... 18 */ 19 20 #include <cups/cups.h> 21 #include "string-private.h" 22 #include "debug-private.h" 23 #include "array-private.h" 24 25 26 /* 27 * Limits... 28 */ 29 30 #define _CUPS_MAXSAVE 32 /**** Maximum number of saves ****/ 31 32 33 /* 34 * Types and structures... 35 */ 36 37 struct _cups_array_s /**** CUPS array structure ****/ 38 { 39 /* 40 * The current implementation uses an insertion sort into an array of 41 * sorted pointers. We leave the array type private/opaque so that we 42 * can change the underlying implementation without affecting the users 43 * of this API. 44 */ 45 46 int num_elements, /* Number of array elements */ 47 alloc_elements, /* Allocated array elements */ 48 current, /* Current element */ 49 insert, /* Last inserted element */ 50 unique, /* Are all elements unique? */ 51 num_saved, /* Number of saved elements */ 52 saved[_CUPS_MAXSAVE]; 53 /* Saved elements */ 54 void **elements; /* Array elements */ 55 cups_array_func_t compare; /* Element comparison function */ 56 void *data; /* User data passed to compare */ 57 cups_ahash_func_t hashfunc; /* Hash function */ 58 int hashsize, /* Size of hash */ 59 *hash; /* Hash array */ 60 cups_acopy_func_t copyfunc; /* Copy function */ 61 cups_afree_func_t freefunc; /* Free function */ 62 }; 63 64 65 /* 66 * Local functions... 67 */ 68 69 static int cups_array_add(cups_array_t *a, void *e, int insert); 70 static int cups_array_find(cups_array_t *a, void *e, int prev, int *rdiff); 71 72 73 /* 74 * 'cupsArrayAdd()' - Add an element to the array. 75 * 76 * When adding an element to a sorted array, non-unique elements are 77 * appended at the end of the run of identical elements. For unsorted arrays, 78 * the element is appended to the end of the array. 79 * 80 * @since CUPS 1.2/macOS 10.5@ 81 */ 82 83 int /* O - 1 on success, 0 on failure */ 84 cupsArrayAdd(cups_array_t *a, /* I - Array */ 85 void *e) /* I - Element */ 86 { 87 DEBUG_printf(("2cupsArrayAdd(a=%p, e=%p)", (void *)a, e)); 88 89 /* 90 * Range check input... 91 */ 92 93 if (!a || !e) 94 { 95 DEBUG_puts("3cupsArrayAdd: returning 0"); 96 return (0); 97 } 98 99 /* 100 * Append the element... 101 */ 102 103 return (cups_array_add(a, e, 0)); 104 } 105 106 107 /* 108 * '_cupsArrayAddStrings()' - Add zero or more delimited strings to an array. 109 * 110 * Note: The array MUST be created using the @link _cupsArrayNewStrings@ 111 * function. Duplicate strings are NOT added. If the string pointer "s" is NULL 112 * or the empty string, no strings are added to the array. 113 */ 114 115 int /* O - 1 on success, 0 on failure */ 116 _cupsArrayAddStrings(cups_array_t *a, /* I - Array */ 117 const char *s, /* I - Delimited strings or NULL */ 118 char delim)/* I - Delimiter character */ 119 { 120 char *buffer, /* Copy of string */ 121 *start, /* Start of string */ 122 *end; /* End of string */ 123 int status = 1; /* Status of add */ 124 125 126 DEBUG_printf(("_cupsArrayAddStrings(a=%p, s=\"%s\", delim='%c')", (void *)a, s, delim)); 127 128 if (!a || !s || !*s) 129 { 130 DEBUG_puts("1_cupsArrayAddStrings: Returning 0"); 131 return (0); 132 } 133 134 if (delim == ' ') 135 { 136 /* 137 * Skip leading whitespace... 138 */ 139 140 DEBUG_puts("1_cupsArrayAddStrings: Skipping leading whitespace."); 141 142 while (*s && isspace(*s & 255)) 143 s ++; 144 145 DEBUG_printf(("1_cupsArrayAddStrings: Remaining string \"%s\".", s)); 146 } 147 148 if (!strchr(s, delim) && 149 (delim != ' ' || (!strchr(s, '\t') && !strchr(s, '\n')))) 150 { 151 /* 152 * String doesn't contain a delimiter, so add it as a single value... 153 */ 154 155 DEBUG_puts("1_cupsArrayAddStrings: No delimiter seen, adding a single " 156 "value."); 157 158 if (!cupsArrayFind(a, (void *)s)) 159 status = cupsArrayAdd(a, (void *)s); 160 } 161 else if ((buffer = strdup(s)) == NULL) 162 { 163 DEBUG_puts("1_cupsArrayAddStrings: Unable to duplicate string."); 164 status = 0; 165 } 166 else 167 { 168 for (start = end = buffer; *end; start = end) 169 { 170 /* 171 * Find the end of the current delimited string and see if we need to add 172 * it... 173 */ 174 175 if (delim == ' ') 176 { 177 while (*end && !isspace(*end & 255)) 178 end ++; 179 while (*end && isspace(*end & 255)) 180 *end++ = '\0'; 181 } 182 else if ((end = strchr(start, delim)) != NULL) 183 *end++ = '\0'; 184 else 185 end = start + strlen(start); 186 187 DEBUG_printf(("1_cupsArrayAddStrings: Adding \"%s\", end=\"%s\"", start, 188 end)); 189 190 if (!cupsArrayFind(a, start)) 191 status &= cupsArrayAdd(a, start); 192 } 193 194 free(buffer); 195 } 196 197 DEBUG_printf(("1_cupsArrayAddStrings: Returning %d.", status)); 198 199 return (status); 200 } 201 202 203 /* 204 * 'cupsArrayClear()' - Clear the array. 205 * 206 * This function is equivalent to removing all elements in the array. 207 * The caller is responsible for freeing the memory used by the 208 * elements themselves. 209 * 210 * @since CUPS 1.2/macOS 10.5@ 211 */ 212 213 void 214 cupsArrayClear(cups_array_t *a) /* I - Array */ 215 { 216 /* 217 * Range check input... 218 */ 219 220 if (!a) 221 return; 222 223 /* 224 * Free the existing elements as needed.. 225 */ 226 227 if (a->freefunc) 228 { 229 int i; /* Looping var */ 230 void **e; /* Current element */ 231 232 for (i = a->num_elements, e = a->elements; i > 0; i --, e ++) 233 (a->freefunc)(*e, a->data); 234 } 235 236 /* 237 * Set the number of elements to 0; we don't actually free the memory 238 * here - that is done in cupsArrayDelete()... 239 */ 240 241 a->num_elements = 0; 242 a->current = -1; 243 a->insert = -1; 244 a->unique = 1; 245 a->num_saved = 0; 246 } 247 248 249 /* 250 * 'cupsArrayCount()' - Get the number of elements in the array. 251 * 252 * @since CUPS 1.2/macOS 10.5@ 253 */ 254 255 int /* O - Number of elements */ 256 cupsArrayCount(cups_array_t *a) /* I - Array */ 257 { 258 /* 259 * Range check input... 260 */ 261 262 if (!a) 263 return (0); 264 265 /* 266 * Return the number of elements... 267 */ 268 269 return (a->num_elements); 270 } 271 272 273 /* 274 * 'cupsArrayCurrent()' - Return the current element in the array. 275 * 276 * The current element is undefined until you call @link cupsArrayFind@, 277 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@. 278 * 279 * @since CUPS 1.2/macOS 10.5@ 280 */ 281 282 void * /* O - Element */ 283 cupsArrayCurrent(cups_array_t *a) /* I - Array */ 284 { 285 /* 286 * Range check input... 287 */ 288 289 if (!a) 290 return (NULL); 291 292 /* 293 * Return the current element... 294 */ 295 296 if (a->current >= 0 && a->current < a->num_elements) 297 return (a->elements[a->current]); 298 else 299 return (NULL); 300 } 301 302 303 /* 304 * 'cupsArrayDelete()' - Free all memory used by the array. 305 * 306 * The caller is responsible for freeing the memory used by the 307 * elements themselves. 308 * 309 * @since CUPS 1.2/macOS 10.5@ 310 */ 311 312 void 313 cupsArrayDelete(cups_array_t *a) /* I - Array */ 314 { 315 /* 316 * Range check input... 317 */ 318 319 if (!a) 320 return; 321 322 /* 323 * Free the elements if we have a free function (otherwise the caller is 324 * responsible for doing the dirty work...) 325 */ 326 327 if (a->freefunc) 328 { 329 int i; /* Looping var */ 330 void **e; /* Current element */ 331 332 for (i = a->num_elements, e = a->elements; i > 0; i --, e ++) 333 (a->freefunc)(*e, a->data); 334 } 335 336 /* 337 * Free the array of element pointers... 338 */ 339 340 if (a->alloc_elements) 341 free(a->elements); 342 343 if (a->hashsize) 344 free(a->hash); 345 346 free(a); 347 } 348 349 350 /* 351 * 'cupsArrayDup()' - Duplicate the array. 352 * 353 * @since CUPS 1.2/macOS 10.5@ 354 */ 355 356 cups_array_t * /* O - Duplicate array */ 357 cupsArrayDup(cups_array_t *a) /* I - Array */ 358 { 359 cups_array_t *da; /* Duplicate array */ 360 361 362 /* 363 * Range check input... 364 */ 365 366 if (!a) 367 return (NULL); 368 369 /* 370 * Allocate memory for the array... 371 */ 372 373 da = calloc(1, sizeof(cups_array_t)); 374 if (!da) 375 return (NULL); 376 377 da->compare = a->compare; 378 da->data = a->data; 379 da->current = a->current; 380 da->insert = a->insert; 381 da->unique = a->unique; 382 da->num_saved = a->num_saved; 383 384 memcpy(da->saved, a->saved, sizeof(a->saved)); 385 386 if (a->num_elements) 387 { 388 /* 389 * Allocate memory for the elements... 390 */ 391 392 da->elements = malloc((size_t)a->num_elements * sizeof(void *)); 393 if (!da->elements) 394 { 395 free(da); 396 return (NULL); 397 } 398 399 /* 400 * Copy the element pointers... 401 */ 402 403 if (a->copyfunc) 404 { 405 /* 406 * Use the copy function to make a copy of each element... 407 */ 408 409 int i; /* Looping var */ 410 411 for (i = 0; i < a->num_elements; i ++) 412 da->elements[i] = (a->copyfunc)(a->elements[i], a->data); 413 } 414 else 415 { 416 /* 417 * Just copy raw pointers... 418 */ 419 420 memcpy(da->elements, a->elements, (size_t)a->num_elements * sizeof(void *)); 421 } 422 423 da->num_elements = a->num_elements; 424 da->alloc_elements = a->num_elements; 425 } 426 427 /* 428 * Return the new array... 429 */ 430 431 return (da); 432 } 433 434 435 /* 436 * 'cupsArrayFind()' - Find an element in the array. 437 * 438 * @since CUPS 1.2/macOS 10.5@ 439 */ 440 441 void * /* O - Element found or @code NULL@ */ 442 cupsArrayFind(cups_array_t *a, /* I - Array */ 443 void *e) /* I - Element */ 444 { 445 int current, /* Current element */ 446 diff, /* Difference */ 447 hash; /* Hash index */ 448 449 450 /* 451 * Range check input... 452 */ 453 454 if (!a || !e) 455 return (NULL); 456 457 /* 458 * See if we have any elements... 459 */ 460 461 if (!a->num_elements) 462 return (NULL); 463 464 /* 465 * Yes, look for a match... 466 */ 467 468 if (a->hash) 469 { 470 hash = (*(a->hashfunc))(e, a->data); 471 472 if (hash < 0 || hash >= a->hashsize) 473 { 474 current = a->current; 475 hash = -1; 476 } 477 else 478 { 479 current = a->hash[hash]; 480 481 if (current < 0 || current >= a->num_elements) 482 current = a->current; 483 } 484 } 485 else 486 { 487 current = a->current; 488 hash = -1; 489 } 490 491 current = cups_array_find(a, e, current, &diff); 492 if (!diff) 493 { 494 /* 495 * Found a match! If the array does not contain unique values, find 496 * the first element that is the same... 497 */ 498 499 if (!a->unique && a->compare) 500 { 501 /* 502 * The array is not unique, find the first match... 503 */ 504 505 while (current > 0 && !(*(a->compare))(e, a->elements[current - 1], 506 a->data)) 507 current --; 508 } 509 510 a->current = current; 511 512 if (hash >= 0) 513 a->hash[hash] = current; 514 515 return (a->elements[current]); 516 } 517 else 518 { 519 /* 520 * No match... 521 */ 522 523 a->current = -1; 524 525 return (NULL); 526 } 527 } 528 529 530 /* 531 * 'cupsArrayFirst()' - Get the first element in the array. 532 * 533 * @since CUPS 1.2/macOS 10.5@ 534 */ 535 536 void * /* O - First element or @code NULL@ if the array is empty */ 537 cupsArrayFirst(cups_array_t *a) /* I - Array */ 538 { 539 /* 540 * Range check input... 541 */ 542 543 if (!a) 544 return (NULL); 545 546 /* 547 * Return the first element... 548 */ 549 550 a->current = 0; 551 552 return (cupsArrayCurrent(a)); 553 } 554 555 556 /* 557 * 'cupsArrayGetIndex()' - Get the index of the current element. 558 * 559 * The current element is undefined until you call @link cupsArrayFind@, 560 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@. 561 * 562 * @since CUPS 1.3/macOS 10.5@ 563 */ 564 565 int /* O - Index of the current element, starting at 0 */ 566 cupsArrayGetIndex(cups_array_t *a) /* I - Array */ 567 { 568 if (!a) 569 return (-1); 570 else 571 return (a->current); 572 } 573 574 575 /* 576 * 'cupsArrayGetInsert()' - Get the index of the last inserted element. 577 * 578 * @since CUPS 1.3/macOS 10.5@ 579 */ 580 581 int /* O - Index of the last inserted element, starting at 0 */ 582 cupsArrayGetInsert(cups_array_t *a) /* I - Array */ 583 { 584 if (!a) 585 return (-1); 586 else 587 return (a->insert); 588 } 589 590 591 /* 592 * 'cupsArrayIndex()' - Get the N-th element in the array. 593 * 594 * @since CUPS 1.2/macOS 10.5@ 595 */ 596 597 void * /* O - N-th element or @code NULL@ */ 598 cupsArrayIndex(cups_array_t *a, /* I - Array */ 599 int n) /* I - Index into array, starting at 0 */ 600 { 601 if (!a) 602 return (NULL); 603 604 a->current = n; 605 606 return (cupsArrayCurrent(a)); 607 } 608 609 610 /* 611 * 'cupsArrayInsert()' - Insert an element in the array. 612 * 613 * When inserting an element in a sorted array, non-unique elements are 614 * inserted at the beginning of the run of identical elements. For unsorted 615 * arrays, the element is inserted at the beginning of the array. 616 * 617 * @since CUPS 1.2/macOS 10.5@ 618 */ 619 620 int /* O - 0 on failure, 1 on success */ 621 cupsArrayInsert(cups_array_t *a, /* I - Array */ 622 void *e) /* I - Element */ 623 { 624 DEBUG_printf(("2cupsArrayInsert(a=%p, e=%p)", (void *)a, e)); 625 626 /* 627 * Range check input... 628 */ 629 630 if (!a || !e) 631 { 632 DEBUG_puts("3cupsArrayInsert: returning 0"); 633 return (0); 634 } 635 636 /* 637 * Insert the element... 638 */ 639 640 return (cups_array_add(a, e, 1)); 641 } 642 643 644 /* 645 * 'cupsArrayLast()' - Get the last element in the array. 646 * 647 * @since CUPS 1.2/macOS 10.5@ 648 */ 649 650 void * /* O - Last element or @code NULL@ if the array is empty */ 651 cupsArrayLast(cups_array_t *a) /* I - Array */ 652 { 653 /* 654 * Range check input... 655 */ 656 657 if (!a) 658 return (NULL); 659 660 /* 661 * Return the last element... 662 */ 663 664 a->current = a->num_elements - 1; 665 666 return (cupsArrayCurrent(a)); 667 } 668 669 670 /* 671 * 'cupsArrayNew()' - Create a new array. 672 * 673 * The comparison function ("f") is used to create a sorted array. The function 674 * receives pointers to two elements and the user data pointer ("d") - the user 675 * data pointer argument can safely be omitted when not required so functions 676 * like @code strcmp@ can be used for sorted string arrays. 677 * 678 * @since CUPS 1.2/macOS 10.5@ 679 */ 680 681 cups_array_t * /* O - Array */ 682 cupsArrayNew(cups_array_func_t f, /* I - Comparison function or @code NULL@ for an unsorted array */ 683 void *d) /* I - User data pointer or @code NULL@ */ 684 { 685 return (cupsArrayNew3(f, d, 0, 0, 0, 0)); 686 } 687 688 689 /* 690 * 'cupsArrayNew2()' - Create a new array with hash. 691 * 692 * The comparison function ("f") is used to create a sorted array. The function 693 * receives pointers to two elements and the user data pointer ("d") - the user 694 * data pointer argument can safely be omitted when not required so functions 695 * like @code strcmp@ can be used for sorted string arrays. 696 * 697 * The hash function ("h") is used to implement cached lookups with the 698 * specified hash size ("hsize"). 699 * 700 * @since CUPS 1.3/macOS 10.5@ 701 */ 702 703 cups_array_t * /* O - Array */ 704 cupsArrayNew2(cups_array_func_t f, /* I - Comparison function or @code NULL@ for an unsorted array */ 705 void *d, /* I - User data or @code NULL@ */ 706 cups_ahash_func_t h, /* I - Hash function or @code NULL@ for unhashed lookups */ 707 int hsize) /* I - Hash size (>= 0) */ 708 { 709 return (cupsArrayNew3(f, d, h, hsize, 0, 0)); 710 } 711 712 713 /* 714 * 'cupsArrayNew3()' - Create a new array with hash and/or free function. 715 * 716 * The comparison function ("f") is used to create a sorted array. The function 717 * receives pointers to two elements and the user data pointer ("d") - the user 718 * data pointer argument can safely be omitted when not required so functions 719 * like @code strcmp@ can be used for sorted string arrays. 720 * 721 * The hash function ("h") is used to implement cached lookups with the 722 * specified hash size ("hsize"). 723 * 724 * The copy function ("cf") is used to automatically copy/retain elements when 725 * added or the array is copied. 726 * 727 * The free function ("cf") is used to automatically free/release elements when 728 * removed or the array is deleted. 729 * 730 * @since CUPS 1.5/macOS 10.7@ 731 */ 732 733 cups_array_t * /* O - Array */ 734 cupsArrayNew3(cups_array_func_t f, /* I - Comparison function or @code NULL@ for an unsorted array */ 735 void *d, /* I - User data or @code NULL@ */ 736 cups_ahash_func_t h, /* I - Hash function or @code NULL@ for unhashed lookups */ 737 int hsize, /* I - Hash size (>= 0) */ 738 cups_acopy_func_t cf, /* I - Copy function */ 739 cups_afree_func_t ff) /* I - Free function */ 740 { 741 cups_array_t *a; /* Array */ 742 743 744 /* 745 * Allocate memory for the array... 746 */ 747 748 a = calloc(1, sizeof(cups_array_t)); 749 if (!a) 750 return (NULL); 751 752 a->compare = f; 753 a->data = d; 754 a->current = -1; 755 a->insert = -1; 756 a->num_saved = 0; 757 a->unique = 1; 758 759 if (hsize > 0 && h) 760 { 761 a->hashfunc = h; 762 a->hashsize = hsize; 763 a->hash = malloc((size_t)hsize * sizeof(int)); 764 765 if (!a->hash) 766 { 767 free(a); 768 return (NULL); 769 } 770 771 memset(a->hash, -1, (size_t)hsize * sizeof(int)); 772 } 773 774 a->copyfunc = cf; 775 a->freefunc = ff; 776 777 return (a); 778 } 779 780 781 /* 782 * '_cupsArrayNewStrings()' - Create a new array of comma-delimited strings. 783 * 784 * Note: The array automatically manages copies of the strings passed. If the 785 * string pointer "s" is NULL or the empty string, no strings are added to the 786 * newly created array. 787 */ 788 789 cups_array_t * /* O - Array */ 790 _cupsArrayNewStrings(const char *s, /* I - Delimited strings or NULL */ 791 char delim) /* I - Delimiter character */ 792 { 793 cups_array_t *a; /* Array */ 794 795 796 if ((a = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, 797 (cups_acopy_func_t)_cupsStrAlloc, 798 (cups_afree_func_t)_cupsStrFree)) != NULL) 799 _cupsArrayAddStrings(a, s, delim); 800 801 return (a); 802 } 803 804 805 /* 806 * 'cupsArrayNext()' - Get the next element in the array. 807 * 808 * This function is equivalent to "cupsArrayIndex(a, cupsArrayGetIndex(a) + 1)". 809 * 810 * The next element is undefined until you call @link cupsArrayFind@, 811 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@ 812 * to set the current element. 813 * 814 * @since CUPS 1.2/macOS 10.5@ 815 */ 816 817 void * /* O - Next element or @code NULL@ */ 818 cupsArrayNext(cups_array_t *a) /* I - Array */ 819 { 820 /* 821 * Range check input... 822 */ 823 824 if (!a) 825 return (NULL); 826 827 /* 828 * Return the next element... 829 */ 830 831 if (a->current < a->num_elements) 832 a->current ++; 833 834 return (cupsArrayCurrent(a)); 835 } 836 837 838 /* 839 * 'cupsArrayPrev()' - Get the previous element in the array. 840 * 841 * This function is equivalent to "cupsArrayIndex(a, cupsArrayGetIndex(a) - 1)". 842 * 843 * The previous element is undefined until you call @link cupsArrayFind@, 844 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@ 845 * to set the current element. 846 * 847 * @since CUPS 1.2/macOS 10.5@ 848 */ 849 850 void * /* O - Previous element or @code NULL@ */ 851 cupsArrayPrev(cups_array_t *a) /* I - Array */ 852 { 853 /* 854 * Range check input... 855 */ 856 857 if (!a) 858 return (NULL); 859 860 /* 861 * Return the previous element... 862 */ 863 864 if (a->current >= 0) 865 a->current --; 866 867 return (cupsArrayCurrent(a)); 868 } 869 870 871 /* 872 * 'cupsArrayRemove()' - Remove an element from the array. 873 * 874 * If more than one element matches "e", only the first matching element is 875 * removed. 876 * 877 * The caller is responsible for freeing the memory used by the 878 * removed element. 879 * 880 * @since CUPS 1.2/macOS 10.5@ 881 */ 882 883 int /* O - 1 on success, 0 on failure */ 884 cupsArrayRemove(cups_array_t *a, /* I - Array */ 885 void *e) /* I - Element */ 886 { 887 ssize_t i, /* Looping var */ 888 current; /* Current element */ 889 int diff; /* Difference */ 890 891 892 /* 893 * Range check input... 894 */ 895 896 if (!a || !e) 897 return (0); 898 899 /* 900 * See if the element is in the array... 901 */ 902 903 if (!a->num_elements) 904 return (0); 905 906 current = cups_array_find(a, e, a->current, &diff); 907 if (diff) 908 return (0); 909 910 /* 911 * Yes, now remove it... 912 */ 913 914 a->num_elements --; 915 916 if (a->freefunc) 917 (a->freefunc)(a->elements[current], a->data); 918 919 if (current < a->num_elements) 920 memmove(a->elements + current, a->elements + current + 1, 921 (size_t)(a->num_elements - current) * sizeof(void *)); 922 923 if (current <= a->current) 924 a->current --; 925 926 if (current < a->insert) 927 a->insert --; 928 else if (current == a->insert) 929 a->insert = -1; 930 931 for (i = 0; i < a->num_saved; i ++) 932 if (current <= a->saved[i]) 933 a->saved[i] --; 934 935 if (a->num_elements <= 1) 936 a->unique = 1; 937 938 return (1); 939 } 940 941 942 /* 943 * 'cupsArrayRestore()' - Reset the current element to the last @link cupsArraySave@. 944 * 945 * @since CUPS 1.2/macOS 10.5@ 946 */ 947 948 void * /* O - New current element */ 949 cupsArrayRestore(cups_array_t *a) /* I - Array */ 950 { 951 if (!a) 952 return (NULL); 953 954 if (a->num_saved <= 0) 955 return (NULL); 956 957 a->num_saved --; 958 a->current = a->saved[a->num_saved]; 959 960 if (a->current >= 0 && a->current < a->num_elements) 961 return (a->elements[a->current]); 962 else 963 return (NULL); 964 } 965 966 967 /* 968 * 'cupsArraySave()' - Mark the current element for a later @link cupsArrayRestore@. 969 * 970 * The current element is undefined until you call @link cupsArrayFind@, 971 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@ 972 * to set the current element. 973 * 974 * The save/restore stack is guaranteed to be at least 32 elements deep. 975 * 976 * @since CUPS 1.2/macOS 10.5@ 977 */ 978 979 int /* O - 1 on success, 0 on failure */ 980 cupsArraySave(cups_array_t *a) /* I - Array */ 981 { 982 if (!a) 983 return (0); 984 985 if (a->num_saved >= _CUPS_MAXSAVE) 986 return (0); 987 988 a->saved[a->num_saved] = a->current; 989 a->num_saved ++; 990 991 return (1); 992 } 993 994 995 /* 996 * 'cupsArrayUserData()' - Return the user data for an array. 997 * 998 * @since CUPS 1.2/macOS 10.5@ 999 */ 1000 1001 void * /* O - User data */ 1002 cupsArrayUserData(cups_array_t *a) /* I - Array */ 1003 { 1004 if (a) 1005 return (a->data); 1006 else 1007 return (NULL); 1008 } 1009 1010 1011 /* 1012 * 'cups_array_add()' - Insert or append an element to the array. 1013 * 1014 * @since CUPS 1.2/macOS 10.5@ 1015 */ 1016 1017 static int /* O - 1 on success, 0 on failure */ 1018 cups_array_add(cups_array_t *a, /* I - Array */ 1019 void *e, /* I - Element to add */ 1020 int insert) /* I - 1 = insert, 0 = append */ 1021 { 1022 int i, /* Looping var */ 1023 current; /* Current element */ 1024 int diff; /* Comparison with current element */ 1025 1026 1027 DEBUG_printf(("7cups_array_add(a=%p, e=%p, insert=%d)", (void *)a, e, insert)); 1028 1029 /* 1030 * Verify we have room for the new element... 1031 */ 1032 1033 if (a->num_elements >= a->alloc_elements) 1034 { 1035 /* 1036 * Allocate additional elements; start with 16 elements, then 1037 * double the size until 1024 elements, then add 1024 elements 1038 * thereafter... 1039 */ 1040 1041 void **temp; /* New array elements */ 1042 int count; /* New allocation count */ 1043 1044 1045 if (a->alloc_elements == 0) 1046 { 1047 count = 16; 1048 temp = malloc((size_t)count * sizeof(void *)); 1049 } 1050 else 1051 { 1052 if (a->alloc_elements < 1024) 1053 count = a->alloc_elements * 2; 1054 else 1055 count = a->alloc_elements + 1024; 1056 1057 temp = realloc(a->elements, (size_t)count * sizeof(void *)); 1058 } 1059 1060 DEBUG_printf(("9cups_array_add: count=" CUPS_LLFMT, CUPS_LLCAST count)); 1061 1062 if (!temp) 1063 { 1064 DEBUG_puts("9cups_array_add: allocation failed, returning 0"); 1065 return (0); 1066 } 1067 1068 a->alloc_elements = count; 1069 a->elements = temp; 1070 } 1071 1072 /* 1073 * Find the insertion point for the new element; if there is no 1074 * compare function or elements, just add it to the beginning or end... 1075 */ 1076 1077 if (!a->num_elements || !a->compare) 1078 { 1079 /* 1080 * No elements or comparison function, insert/append as needed... 1081 */ 1082 1083 if (insert) 1084 current = 0; /* Insert at beginning */ 1085 else 1086 current = a->num_elements; /* Append to the end */ 1087 } 1088 else 1089 { 1090 /* 1091 * Do a binary search for the insertion point... 1092 */ 1093 1094 current = cups_array_find(a, e, a->insert, &diff); 1095 1096 if (diff > 0) 1097 { 1098 /* 1099 * Insert after the current element... 1100 */ 1101 1102 current ++; 1103 } 1104 else if (!diff) 1105 { 1106 /* 1107 * Compared equal, make sure we add to the begining or end of 1108 * the current run of equal elements... 1109 */ 1110 1111 a->unique = 0; 1112 1113 if (insert) 1114 { 1115 /* 1116 * Insert at beginning of run... 1117 */ 1118 1119 while (current > 0 && !(*(a->compare))(e, a->elements[current - 1], 1120 a->data)) 1121 current --; 1122 } 1123 else 1124 { 1125 /* 1126 * Append at end of run... 1127 */ 1128 1129 do 1130 { 1131 current ++; 1132 } 1133 while (current < a->num_elements && 1134 !(*(a->compare))(e, a->elements[current], a->data)); 1135 } 1136 } 1137 } 1138 1139 /* 1140 * Insert or append the element... 1141 */ 1142 1143 if (current < a->num_elements) 1144 { 1145 /* 1146 * Shift other elements to the right... 1147 */ 1148 1149 memmove(a->elements + current + 1, a->elements + current, 1150 (size_t)(a->num_elements - current) * sizeof(void *)); 1151 1152 if (a->current >= current) 1153 a->current ++; 1154 1155 for (i = 0; i < a->num_saved; i ++) 1156 if (a->saved[i] >= current) 1157 a->saved[i] ++; 1158 1159 DEBUG_printf(("9cups_array_add: insert element at index " CUPS_LLFMT, CUPS_LLCAST current)); 1160 } 1161 #ifdef DEBUG 1162 else 1163 DEBUG_printf(("9cups_array_add: append element at " CUPS_LLFMT, CUPS_LLCAST current)); 1164 #endif /* DEBUG */ 1165 1166 if (a->copyfunc) 1167 { 1168 if ((a->elements[current] = (a->copyfunc)(e, a->data)) == NULL) 1169 { 1170 DEBUG_puts("8cups_array_add: Copy function returned NULL, returning 0"); 1171 return (0); 1172 } 1173 } 1174 else 1175 a->elements[current] = e; 1176 1177 a->num_elements ++; 1178 a->insert = current; 1179 1180 #ifdef DEBUG 1181 for (current = 0; current < a->num_elements; current ++) 1182 DEBUG_printf(("9cups_array_add: a->elements[" CUPS_LLFMT "]=%p", CUPS_LLCAST current, a->elements[current])); 1183 #endif /* DEBUG */ 1184 1185 DEBUG_puts("9cups_array_add: returning 1"); 1186 1187 return (1); 1188 } 1189 1190 1191 /* 1192 * 'cups_array_find()' - Find an element in the array. 1193 */ 1194 1195 static int /* O - Index of match */ 1196 cups_array_find(cups_array_t *a, /* I - Array */ 1197 void *e, /* I - Element */ 1198 int prev, /* I - Previous index */ 1199 int *rdiff) /* O - Difference of match */ 1200 { 1201 int left, /* Left side of search */ 1202 right, /* Right side of search */ 1203 current, /* Current element */ 1204 diff; /* Comparison with current element */ 1205 1206 1207 DEBUG_printf(("7cups_array_find(a=%p, e=%p, prev=%d, rdiff=%p)", (void *)a, e, prev, (void *)rdiff)); 1208 1209 if (a->compare) 1210 { 1211 /* 1212 * Do a binary search for the element... 1213 */ 1214 1215 DEBUG_puts("9cups_array_find: binary search"); 1216 1217 if (prev >= 0 && prev < a->num_elements) 1218 { 1219 /* 1220 * Start search on either side of previous... 1221 */ 1222 1223 if ((diff = (*(a->compare))(e, a->elements[prev], a->data)) == 0 || 1224 (diff < 0 && prev == 0) || 1225 (diff > 0 && prev == (a->num_elements - 1))) 1226 { 1227 /* 1228 * Exact or edge match, return it! 1229 */ 1230 1231 DEBUG_printf(("9cups_array_find: Returning %d, diff=%d", prev, diff)); 1232 1233 *rdiff = diff; 1234 1235 return (prev); 1236 } 1237 else if (diff < 0) 1238 { 1239 /* 1240 * Start with previous on right side... 1241 */ 1242 1243 left = 0; 1244 right = prev; 1245 } 1246 else 1247 { 1248 /* 1249 * Start wih previous on left side... 1250 */ 1251 1252 left = prev; 1253 right = a->num_elements - 1; 1254 } 1255 } 1256 else 1257 { 1258 /* 1259 * Start search in the middle... 1260 */ 1261 1262 left = 0; 1263 right = a->num_elements - 1; 1264 } 1265 1266 do 1267 { 1268 current = (left + right) / 2; 1269 diff = (*(a->compare))(e, a->elements[current], a->data); 1270 1271 DEBUG_printf(("9cups_array_find: left=%d, right=%d, current=%d, diff=%d", 1272 left, right, current, diff)); 1273 1274 if (diff == 0) 1275 break; 1276 else if (diff < 0) 1277 right = current; 1278 else 1279 left = current; 1280 } 1281 while ((right - left) > 1); 1282 1283 if (diff != 0) 1284 { 1285 /* 1286 * Check the last 1 or 2 elements... 1287 */ 1288 1289 if ((diff = (*(a->compare))(e, a->elements[left], a->data)) <= 0) 1290 current = left; 1291 else 1292 { 1293 diff = (*(a->compare))(e, a->elements[right], a->data); 1294 current = right; 1295 } 1296 } 1297 } 1298 else 1299 { 1300 /* 1301 * Do a linear pointer search... 1302 */ 1303 1304 DEBUG_puts("9cups_array_find: linear search"); 1305 1306 diff = 1; 1307 1308 for (current = 0; current < a->num_elements; current ++) 1309 if (a->elements[current] == e) 1310 { 1311 diff = 0; 1312 break; 1313 } 1314 } 1315 1316 /* 1317 * Return the closest element and the difference... 1318 */ 1319 1320 DEBUG_printf(("8cups_array_find: Returning %d, diff=%d", current, diff)); 1321 1322 *rdiff = diff; 1323 1324 return (current); 1325 } 1326