]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/array.c
Update all references to OS X to macOS.
[thirdparty/cups.git] / cups / array.c
CommitLineData
ef416fc2 1/*
7e86f2f6 2 * Sorted array routines for CUPS.
ef416fc2 3 *
7e86f2f6
MS
4 * Copyright 2007-2014 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products.
ef416fc2 6 *
7e86f2f6
MS
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 * file is missing or damaged, see the license at "http://www.cups.org/".
ef416fc2 12 *
7e86f2f6 13 * This file is subject to the Apple OS-Developed Software exception.
ef416fc2 14 */
15
16/*
17 * Include necessary headers...
18 */
19
33c9220c 20#include <cups/cups.h>
71e16022
MS
21#include "string-private.h"
22#include "debug-private.h"
eac3a0a0 23#include "array-private.h"
ef416fc2 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
37struct _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 */
bd7854cb 50 unique, /* Are all elements unique? */
ef416fc2 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 */
b94498cf 57 cups_ahash_func_t hashfunc; /* Hash function */
58 int hashsize, /* Size of hash */
59 *hash; /* Hash array */
10d09e33
MS
60 cups_acopy_func_t copyfunc; /* Copy function */
61 cups_afree_func_t freefunc; /* Free function */
ef416fc2 62};
63
64
65/*
66 * Local functions...
67 */
68
bd7854cb 69static int cups_array_add(cups_array_t *a, void *e, int insert);
70static int cups_array_find(cups_array_t *a, void *e, int prev, int *rdiff);
ef416fc2 71
72
73/*
74 * 'cupsArrayAdd()' - Add an element to the array.
bd7854cb 75 *
76 * When adding an element to a sorted array, non-unique elements are
79e1d494
MS
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.
5a738aea 79 *
8072030b 80 * @since CUPS 1.2/macOS 10.5@
ef416fc2 81 */
82
83int /* O - 1 on success, 0 on failure */
84cupsArrayAdd(cups_array_t *a, /* I - Array */
85 void *e) /* I - Element */
86{
807315e6 87 DEBUG_printf(("2cupsArrayAdd(a=%p, e=%p)", (void *)a, e));
ef416fc2 88
89 /*
90 * Range check input...
91 */
92
93 if (!a || !e)
94 {
e07d4801 95 DEBUG_puts("3cupsArrayAdd: returning 0");
ef416fc2 96 return (0);
97 }
98
99 /*
bd7854cb 100 * Append the element...
ef416fc2 101 */
102
bd7854cb 103 return (cups_array_add(a, e, 0));
ef416fc2 104}
105
106
eac3a0a0 107/*
5a9febac 108 * '_cupsArrayAddStrings()' - Add zero or more delimited strings to an array.
eac3a0a0
MS
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
115int /* O - 1 on success, 0 on failure */
116_cupsArrayAddStrings(cups_array_t *a, /* I - Array */
5a9febac
MS
117 const char *s, /* I - Delimited strings or NULL */
118 char delim)/* I - Delimiter character */
eac3a0a0
MS
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
807315e6 126 DEBUG_printf(("_cupsArrayAddStrings(a=%p, s=\"%s\", delim='%c')", (void *)a, s, delim));
a469f8a5 127
eac3a0a0 128 if (!a || !s || !*s)
a469f8a5
MS
129 {
130 DEBUG_puts("1_cupsArrayAddStrings: Returning 0");
eac3a0a0 131 return (0);
a469f8a5 132 }
eac3a0a0 133
5a9febac
MS
134 if (delim == ' ')
135 {
136 /*
137 * Skip leading whitespace...
138 */
139
a469f8a5
MS
140 DEBUG_puts("1_cupsArrayAddStrings: Skipping leading whitespace.");
141
5a9febac
MS
142 while (*s && isspace(*s & 255))
143 s ++;
a469f8a5
MS
144
145 DEBUG_printf(("1_cupsArrayAddStrings: Remaining string \"%s\".", s));
5a9febac
MS
146 }
147
a469f8a5
MS
148 if (!strchr(s, delim) &&
149 (delim != ' ' || (!strchr(s, '\t') && !strchr(s, '\n'))))
eac3a0a0
MS
150 {
151 /*
5a9febac 152 * String doesn't contain a delimiter, so add it as a single value...
eac3a0a0
MS
153 */
154
a469f8a5
MS
155 DEBUG_puts("1_cupsArrayAddStrings: No delimiter seen, adding a single "
156 "value.");
157
eac3a0a0
MS
158 if (!cupsArrayFind(a, (void *)s))
159 status = cupsArrayAdd(a, (void *)s);
160 }
161 else if ((buffer = strdup(s)) == NULL)
a469f8a5
MS
162 {
163 DEBUG_puts("1_cupsArrayAddStrings: Unable to duplicate string.");
eac3a0a0 164 status = 0;
a469f8a5 165 }
eac3a0a0
MS
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
5a9febac
MS
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)
eac3a0a0
MS
183 *end++ = '\0';
184 else
185 end = start + strlen(start);
186
a469f8a5
MS
187 DEBUG_printf(("1_cupsArrayAddStrings: Adding \"%s\", end=\"%s\"", start,
188 end));
189
eac3a0a0
MS
190 if (!cupsArrayFind(a, start))
191 status &= cupsArrayAdd(a, start);
192 }
193
194 free(buffer);
195 }
196
a469f8a5
MS
197 DEBUG_printf(("1_cupsArrayAddStrings: Returning %d.", status));
198
eac3a0a0
MS
199 return (status);
200}
201
202
ef416fc2 203/*
204 * 'cupsArrayClear()' - Clear the array.
5a738aea 205 *
79e1d494
MS
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 *
8072030b 210 * @since CUPS 1.2/macOS 10.5@
ef416fc2 211 */
212
213void
214cupsArrayClear(cups_array_t *a) /* I - Array */
215{
216 /*
217 * Range check input...
218 */
219
220 if (!a)
221 return;
222
10d09e33
MS
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
ef416fc2 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;
bd7854cb 244 a->unique = 1;
ef416fc2 245 a->num_saved = 0;
246}
247
248
249/*
250 * 'cupsArrayCount()' - Get the number of elements in the array.
5a738aea 251 *
8072030b 252 * @since CUPS 1.2/macOS 10.5@
ef416fc2 253 */
254
255int /* O - Number of elements */
256cupsArrayCount(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.
5a738aea 275 *
79e1d494
MS
276 * The current element is undefined until you call @link cupsArrayFind@,
277 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@.
278 *
8072030b 279 * @since CUPS 1.2/macOS 10.5@
ef416fc2 280 */
281
282void * /* O - Element */
283cupsArrayCurrent(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.
5a738aea 305 *
79e1d494
MS
306 * The caller is responsible for freeing the memory used by the
307 * elements themselves.
308 *
8072030b 309 * @since CUPS 1.2/macOS 10.5@
ef416fc2 310 */
311
312void
313cupsArrayDelete(cups_array_t *a) /* I - Array */
314{
315 /*
316 * Range check input...
317 */
318
319 if (!a)
320 return;
321
322 /*
10d09e33
MS
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...
ef416fc2 338 */
339
340 if (a->alloc_elements)
341 free(a->elements);
342
b94498cf 343 if (a->hashsize)
344 free(a->hash);
345
ef416fc2 346 free(a);
347}
348
349
350/*
351 * 'cupsArrayDup()' - Duplicate the array.
5a738aea 352 *
8072030b 353 * @since CUPS 1.2/macOS 10.5@
ef416fc2 354 */
355
356cups_array_t * /* O - Duplicate array */
357cupsArrayDup(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;
bd7854cb 381 da->unique = a->unique;
ef416fc2 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
7e86f2f6 392 da->elements = malloc((size_t)a->num_elements * sizeof(void *));
ef416fc2 393 if (!da->elements)
394 {
395 free(da);
396 return (NULL);
397 }
398
399 /*
400 * Copy the element pointers...
401 */
402
10d09e33
MS
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
7e86f2f6 420 memcpy(da->elements, a->elements, (size_t)a->num_elements * sizeof(void *));
10d09e33
MS
421 }
422
ef416fc2 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.
5a738aea 437 *
8072030b 438 * @since CUPS 1.2/macOS 10.5@
ef416fc2 439 */
440
5a738aea 441void * /* O - Element found or @code NULL@ */
ef416fc2 442cupsArrayFind(cups_array_t *a, /* I - Array */
443 void *e) /* I - Element */
444{
445 int current, /* Current element */
b94498cf 446 diff, /* Difference */
447 hash; /* Hash index */
ef416fc2 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
b94498cf 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);
ef416fc2 492 if (!diff)
493 {
494 /*
bd7854cb 495 * Found a match! If the array does not contain unique values, find
496 * the first element that is the same...
ef416fc2 497 */
498
bd7854cb 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
ef416fc2 510 a->current = current;
511
b94498cf 512 if (hash >= 0)
513 a->hash[hash] = current;
514
ef416fc2 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.
5a738aea 532 *
8072030b 533 * @since CUPS 1.2/macOS 10.5@
ef416fc2 534 */
535
79e1d494 536void * /* O - First element or @code NULL@ if the array is empty */
ef416fc2 537cupsArrayFirst(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
f7deaa1a 556/*
557 * 'cupsArrayGetIndex()' - Get the index of the current element.
558 *
79e1d494
MS
559 * The current element is undefined until you call @link cupsArrayFind@,
560 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@.
561 *
8072030b 562 * @since CUPS 1.3/macOS 10.5@
f7deaa1a 563 */
564
79e1d494 565int /* O - Index of the current element, starting at 0 */
f7deaa1a 566cupsArrayGetIndex(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 *
8072030b 578 * @since CUPS 1.3/macOS 10.5@
f7deaa1a 579 */
580
79e1d494 581int /* O - Index of the last inserted element, starting at 0 */
f7deaa1a 582cupsArrayGetInsert(cups_array_t *a) /* I - Array */
583{
584 if (!a)
585 return (-1);
586 else
587 return (a->insert);
588}
589
590
ef416fc2 591/*
592 * 'cupsArrayIndex()' - Get the N-th element in the array.
5a738aea 593 *
8072030b 594 * @since CUPS 1.2/macOS 10.5@
ef416fc2 595 */
596
5a738aea 597void * /* O - N-th element or @code NULL@ */
ef416fc2 598cupsArrayIndex(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
fa73b229 610/*
611 * 'cupsArrayInsert()' - Insert an element in the array.
612 *
bd7854cb 613 * When inserting an element in a sorted array, non-unique elements are
79e1d494
MS
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.
5a738aea 616 *
8072030b 617 * @since CUPS 1.2/macOS 10.5@
fa73b229 618 */
619
620int /* O - 0 on failure, 1 on success */
621cupsArrayInsert(cups_array_t *a, /* I - Array */
622 void *e) /* I - Element */
623{
807315e6 624 DEBUG_printf(("2cupsArrayInsert(a=%p, e=%p)", (void *)a, e));
fa73b229 625
626 /*
627 * Range check input...
628 */
629
630 if (!a || !e)
631 {
e07d4801 632 DEBUG_puts("3cupsArrayInsert: returning 0");
fa73b229 633 return (0);
634 }
635
fa73b229 636 /*
637 * Insert the element...
638 */
639
bd7854cb 640 return (cups_array_add(a, e, 1));
fa73b229 641}
642
643
ef416fc2 644/*
645 * 'cupsArrayLast()' - Get the last element in the array.
5a738aea 646 *
8072030b 647 * @since CUPS 1.2/macOS 10.5@
ef416fc2 648 */
649
79e1d494 650void * /* O - Last element or @code NULL@ if the array is empty */
ef416fc2 651cupsArrayLast(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.
5a738aea 672 *
79e1d494
MS
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 *
8072030b 678 * @since CUPS 1.2/macOS 10.5@
ef416fc2 679 */
680
681cups_array_t * /* O - Array */
79e1d494
MS
682cupsArrayNew(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@ */
b94498cf 684{
10d09e33 685 return (cupsArrayNew3(f, d, 0, 0, 0, 0));
b94498cf 686}
687
688
689/*
690 * 'cupsArrayNew2()' - Create a new array with hash.
691 *
79e1d494
MS
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 *
8072030b 700 * @since CUPS 1.3/macOS 10.5@
b94498cf 701 */
702
703cups_array_t * /* O - Array */
79e1d494
MS
704cupsArrayNew2(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) */
10d09e33
MS
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 *
8072030b 730 * @since CUPS 1.5/macOS 10.7@
10d09e33
MS
731 */
732
733cups_array_t * /* O - Array */
734cupsArrayNew3(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 */
ef416fc2 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;
bd7854cb 757 a->unique = 1;
ef416fc2 758
b94498cf 759 if (hsize > 0 && h)
760 {
761 a->hashfunc = h;
762 a->hashsize = hsize;
7e86f2f6 763 a->hash = malloc((size_t)hsize * sizeof(int));
b94498cf 764
765 if (!a->hash)
766 {
767 free(a);
768 return (NULL);
769 }
770
7e86f2f6 771 memset(a->hash, -1, (size_t)hsize * sizeof(int));
b94498cf 772 }
773
10d09e33
MS
774 a->copyfunc = cf;
775 a->freefunc = ff;
776
ef416fc2 777 return (a);
778}
779
780
eac3a0a0
MS
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
789cups_array_t * /* O - Array */
5a9febac
MS
790_cupsArrayNewStrings(const char *s, /* I - Delimited strings or NULL */
791 char delim) /* I - Delimiter character */
eac3a0a0
MS
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)
5a9febac 799 _cupsArrayAddStrings(a, s, delim);
eac3a0a0
MS
800
801 return (a);
802}
803
804
ef416fc2 805/*
806 * 'cupsArrayNext()' - Get the next element in the array.
5a738aea 807 *
79e1d494
MS
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 *
8072030b 814 * @since CUPS 1.2/macOS 10.5@
ef416fc2 815 */
816
5a738aea 817void * /* O - Next element or @code NULL@ */
ef416fc2 818cupsArrayNext(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.
5a738aea 840 *
79e1d494
MS
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 *
8072030b 847 * @since CUPS 1.2/macOS 10.5@
ef416fc2 848 */
849
5a738aea 850void * /* O - Previous element or @code NULL@ */
ef416fc2 851cupsArrayPrev(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.
5a738aea 873 *
79e1d494
MS
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 *
8072030b 880 * @since CUPS 1.2/macOS 10.5@
ef416fc2 881 */
882
883int /* O - 1 on success, 0 on failure */
884cupsArrayRemove(cups_array_t *a, /* I - Array */
885 void *e) /* I - Element */
886{
7e86f2f6
MS
887 ssize_t i, /* Looping var */
888 current; /* Current element */
889 int diff; /* Difference */
ef416fc2 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
bd7854cb 906 current = cups_array_find(a, e, a->current, &diff);
ef416fc2 907 if (diff)
908 return (0);
909
910 /*
911 * Yes, now remove it...
912 */
913
914 a->num_elements --;
915
10d09e33
MS
916 if (a->freefunc)
917 (a->freefunc)(a->elements[current], a->data);
918
ef416fc2 919 if (current < a->num_elements)
920 memmove(a->elements + current, a->elements + current + 1,
7e86f2f6 921 (size_t)(a->num_elements - current) * sizeof(void *));
ef416fc2 922
923 if (current <= a->current)
924 a->current --;
925
f7deaa1a 926 if (current < a->insert)
ef416fc2 927 a->insert --;
f7deaa1a 928 else if (current == a->insert)
929 a->insert = -1;
ef416fc2 930
931 for (i = 0; i < a->num_saved; i ++)
932 if (current <= a->saved[i])
933 a->saved[i] --;
934
bd7854cb 935 if (a->num_elements <= 1)
936 a->unique = 1;
937
ef416fc2 938 return (1);
939}
940
941
942/*
79e1d494 943 * 'cupsArrayRestore()' - Reset the current element to the last @link cupsArraySave@.
5a738aea 944 *
8072030b 945 * @since CUPS 1.2/macOS 10.5@
ef416fc2 946 */
947
948void * /* O - New current element */
949cupsArrayRestore(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/*
79e1d494
MS
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.
ef416fc2 973 *
974 * The save/restore stack is guaranteed to be at least 32 elements deep.
5a738aea 975 *
8072030b 976 * @since CUPS 1.2/macOS 10.5@
ef416fc2 977 */
978
979int /* O - 1 on success, 0 on failure */
980cupsArraySave(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
e1d6a774 995/*
996 * 'cupsArrayUserData()' - Return the user data for an array.
5a738aea 997 *
8072030b 998 * @since CUPS 1.2/macOS 10.5@
e1d6a774 999 */
1000
1001void * /* O - User data */
1002cupsArrayUserData(cups_array_t *a) /* I - Array */
1003{
1004 if (a)
1005 return (a->data);
1006 else
1007 return (NULL);
1008}
1009
1010
ef416fc2 1011/*
eac3a0a0 1012 * 'cups_array_add()' - Insert or append an element to the array.
5a738aea 1013 *
8072030b 1014 * @since CUPS 1.2/macOS 10.5@
bd7854cb 1015 */
1016
1017static int /* O - 1 on success, 0 on failure */
1018cups_array_add(cups_array_t *a, /* I - Array */
1019 void *e, /* I - Element to add */
1020 int insert) /* I - 1 = insert, 0 = append */
1021{
7e86f2f6
MS
1022 int i, /* Looping var */
1023 current; /* Current element */
1024 int diff; /* Comparison with current element */
bd7854cb 1025
1026
807315e6 1027 DEBUG_printf(("7cups_array_add(a=%p, e=%p, insert=%d)", (void *)a, e, insert));
bd7854cb 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;
7e86f2f6 1048 temp = malloc((size_t)count * sizeof(void *));
bd7854cb 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
7e86f2f6 1057 temp = realloc(a->elements, (size_t)count * sizeof(void *));
bd7854cb 1058 }
1059
7e86f2f6 1060 DEBUG_printf(("9cups_array_add: count=" CUPS_LLFMT, CUPS_LLCAST count));
bd7854cb 1061
1062 if (!temp)
1063 {
e07d4801 1064 DEBUG_puts("9cups_array_add: allocation failed, returning 0");
bd7854cb 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,
7e86f2f6 1150 (size_t)(a->num_elements - current) * sizeof(void *));
bd7854cb 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
7e86f2f6 1159 DEBUG_printf(("9cups_array_add: insert element at index " CUPS_LLFMT, CUPS_LLCAST current));
bd7854cb 1160 }
1161#ifdef DEBUG
1162 else
7e86f2f6 1163 DEBUG_printf(("9cups_array_add: append element at " CUPS_LLFMT, CUPS_LLCAST current));
bd7854cb 1164#endif /* DEBUG */
1165
10d09e33
MS
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
bd7854cb 1177 a->num_elements ++;
1178 a->insert = current;
1179
1180#ifdef DEBUG
1181 for (current = 0; current < a->num_elements; current ++)
7e86f2f6 1182 DEBUG_printf(("9cups_array_add: a->elements[" CUPS_LLFMT "]=%p", CUPS_LLCAST current, a->elements[current]));
bd7854cb 1183#endif /* DEBUG */
1184
e07d4801 1185 DEBUG_puts("9cups_array_add: returning 1");
bd7854cb 1186
1187 return (1);
1188}
1189
1190
1191/*
eac3a0a0 1192 * 'cups_array_find()' - Find an element in the array.
ef416fc2 1193 */
1194
1195static int /* O - Index of match */
bd7854cb 1196cups_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 */
ef416fc2 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
807315e6 1207 DEBUG_printf(("7cups_array_find(a=%p, e=%p, prev=%d, rdiff=%p)", (void *)a, e, prev, (void *)rdiff));
ef416fc2 1208
1209 if (a->compare)
1210 {
1211 /*
1212 * Do a binary search for the element...
1213 */
1214
e07d4801 1215 DEBUG_puts("9cups_array_find: binary search");
ef416fc2 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
e07d4801 1231 DEBUG_printf(("9cups_array_find: Returning %d, diff=%d", prev, diff));
ef416fc2 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
e07d4801 1271 DEBUG_printf(("9cups_array_find: left=%d, right=%d, current=%d, diff=%d",
ef416fc2 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
e07d4801 1304 DEBUG_puts("9cups_array_find: linear search");
ef416fc2 1305
a74454a7 1306 diff = 1;
ef416fc2 1307
1308 for (current = 0; current < a->num_elements; current ++)
1309 if (a->elements[current] == e)
a74454a7 1310 {
1311 diff = 0;
ef416fc2 1312 break;
a74454a7 1313 }
ef416fc2 1314 }
1315
1316 /*
1317 * Return the closest element and the difference...
1318 */
1319
e07d4801 1320 DEBUG_printf(("8cups_array_find: Returning %d, diff=%d", current, diff));
ef416fc2 1321
1322 *rdiff = diff;
1323
1324 return (current);
1325}