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