Coverage Report

Created: 2025-10-10 06:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libcups/cups/array.c
Line
Count
Source
1
//
2
// Sorted array routines for CUPS.
3
//
4
// Copyright © 2021-2025 by OpenPrinting.
5
// Copyright © 2007-2014 by Apple Inc.
6
// Copyright © 1997-2007 by Easy Software Products.
7
//
8
// Licensed under Apache License v2.0.  See the file "LICENSE" for more
9
// information.
10
//
11
12
#include <cups/cups.h>
13
#include "string-private.h"
14
#include "debug-internal.h"
15
16
17
//
18
// Limits...
19
//
20
21
0
#define _CUPS_MAXSAVE 32    // Maximum number of saves
22
23
24
//
25
// Types and structures...
26
//
27
28
struct _cups_array_s      // CUPS array structure
29
{
30
  // The current implementation uses an insertion sort into an array of
31
  // sorted pointers.  We leave the array type private/opaque so that we
32
  // can change the underlying implementation without affecting the users
33
  // of this API.
34
35
  size_t    num_elements, // Number of array elements
36
      alloc_elements, // Allocated array elements
37
      current,  // Current element
38
      insert,   // Last inserted element
39
      num_saved,  // Number of saved elements
40
      saved[_CUPS_MAXSAVE];
41
          // Saved elements
42
  void      **elements; // Array elements
43
  cups_array_cb_t compare;  // Element comparison function
44
  bool      unique;   // Are all elements unique?
45
  void      *data;    // User data passed to compare
46
  cups_ahash_cb_t hashfunc; // Hash function
47
  size_t    hashsize, // Size of hash
48
      *hash;    // Hash array
49
  cups_acopy_cb_t copyfunc; // Copy function
50
  cups_afree_cb_t freefunc; // Free function
51
};
52
53
54
//
55
// Local functions...
56
//
57
58
static bool cups_array_add(cups_array_t *a, void *e, bool insert);
59
static size_t cups_array_find(cups_array_t *a, void *e, size_t prev, int *rdiff);
60
61
62
//
63
// 'cupsArrayAdd()' - Add an element to an array.
64
//
65
// This function adds an element to an array.  When adding an element to a
66
// sorted array, non-unique elements are appended at the end of the run of
67
// identical elements.  For unsorted arrays, the element is appended to the end
68
// of the array.
69
//
70
71
bool          // O - `true` on success, `false` on failure
72
cupsArrayAdd(cups_array_t *a,   // I - Array
73
             void         *e)   // I - Element
74
0
{
75
  // Range check input...
76
0
  if (!a || !e)
77
0
    return (false);
78
79
  // Append the element...
80
0
  return (cups_array_add(a, e, false));
81
0
}
82
83
84
//
85
// 'cupsArrayAddStrings()' - Add zero or more delimited strings to an array.
86
//
87
// This function adds zero or more delimited strings to an array created using
88
// the @link cupsArrayNewStrings@ function. Duplicate strings are *not* added.
89
// If the string pointer "s" is `NULL` or the empty string, no strings are
90
// added to the array.  If "delim" is the space character, then all whitespace
91
// is recognized as a delimiter.
92
//
93
// Strings can be delimited by quotes ("foo", 'bar') and curly braces ("{foo}"),
94
// and characters can be escaped using the backslash (\) character.  Outer
95
// quotes are stripped but inner ones are preserved.
96
//
97
98
bool          // O - `true` on success, `false` on failure
99
cupsArrayAddStrings(cups_array_t *a,  // I - Array
100
                    const char   *s,  // I - Delimited strings
101
                    char         delim) // I - Delimiter character
102
0
{
103
0
  char    *buffer,    // Copy of string
104
0
    *start,     // Start of string
105
0
    *end;     // End of string
106
0
  bool    status = true;   // Status of add
107
0
  char    stack[_CUPS_MAXSAVE]; // Quoting stack
108
0
  int   spos = -1;    // Stack position
109
110
111
  // Range check input...
112
0
  if (!a)
113
0
    return (false);
114
115
0
  if (!a || !s || !*s || !delim)
116
0
    return (true);
117
118
0
  if (delim == ' ')
119
0
  {
120
    // Skip leading whitespace...
121
0
    while (*s && isspace(*s & 255))
122
0
      s ++;
123
0
  }
124
125
0
  if (!strchr(s, delim) && (delim != ' ' || (!strchr(s, '\t') && !strchr(s, '\n'))) && *s != '\'' && *s != '\"')
126
0
  {
127
    // String doesn't contain a delimiter, so add it as a single value...
128
0
    if (!cupsArrayFind(a, (void *)s))
129
0
      status = cupsArrayAdd(a, (void *)s);
130
0
  }
131
0
  else if ((buffer = strdup(s)) == NULL)
132
0
  {
133
0
    status = false;
134
0
  }
135
0
  else
136
0
  {
137
0
    for (start = end = buffer; *end; start = end)
138
0
    {
139
      // Find the end of the current delimited string and see if we need to add it...
140
0
      while (*end)
141
0
      {
142
0
        if (*end == '\\' && end[1])
143
0
        {
144
          // Skip escaped character...
145
0
          _cups_strcpy(end, end + 1);
146
0
          end ++;
147
0
        }
148
0
        else if (spos >= 0 && *end == stack[spos])
149
0
        {
150
          // End of quoted value...
151
0
          spos --;
152
0
          if (spos >= 0 || *end == '}')
153
0
            end ++;
154
0
          else
155
0
            _cups_strcpy(end, end + 1);
156
0
        }
157
0
        else if (*end == '{' && spos < (int)(sizeof(stack) - 1))
158
0
        {
159
          // Value in curly braces...
160
0
          spos ++;
161
0
          stack[spos] = '}';
162
0
        }
163
0
        else if ((*end == '\'' || *end == '\"') && spos < (int)(sizeof(stack) - 1))
164
0
        {
165
          // Value in quotes...
166
0
          spos ++;
167
0
          stack[spos] = *end;
168
0
          if (spos == 0)
169
0
            _cups_strcpy(end, end + 1);
170
0
        }
171
0
        else if (*end == delim || (delim == ' ' && isspace(*end & 255)))
172
0
        {
173
          // Delimiter
174
0
          *end++ = '\0';
175
0
          break;
176
0
        }
177
0
        else
178
0
          end ++;
179
0
      }
180
181
0
      if (!cupsArrayFind(a, start))
182
0
        status &= cupsArrayAdd(a, start);
183
0
    }
184
185
0
    free(buffer);
186
0
  }
187
188
0
  return (status);
189
0
}
190
191
192
//
193
// 'cupsArrayClear()' - Clear an array.
194
//
195
// This function is equivalent to removing all elements in the array, so the
196
// free callback (if any) is called for each element that is removed.
197
//
198
199
void
200
cupsArrayClear(cups_array_t *a)   // I - Array
201
0
{
202
  // Range check input...
203
0
  if (!a)
204
0
    return;
205
206
  // Free the existing elements as needed..
207
0
  if (a->freefunc)
208
0
  {
209
0
    size_t  i;      // Looping var
210
0
    void  **e;      // Current element
211
212
0
    for (i = a->num_elements, e = a->elements; i > 0; i --, e ++)
213
0
      (a->freefunc)(*e, a->data);
214
0
  }
215
216
  // Set the number of elements to 0; we don't actually free the memory
217
  // here - that is done in cupsArrayDelete()...
218
0
  a->num_elements = 0;
219
0
  a->current      = SIZE_MAX;
220
0
  a->insert       = SIZE_MAX;
221
0
  a->unique       = true;
222
0
  a->num_saved    = 0;
223
0
}
224
225
226
//
227
// 'cupsArrayDelete()' - Free all memory used by an array.
228
//
229
// This function frees all memory used by an array.  The free callback (if any)
230
// is called for each element in the array.
231
//
232
233
void
234
cupsArrayDelete(cups_array_t *a)  // I - Array
235
0
{
236
  // Range check input...
237
0
  if (!a)
238
0
    return;
239
240
  // Clear the array...
241
0
  cupsArrayClear(a);
242
243
  // Free the other buffers...
244
0
  free(a->elements);
245
0
  free(a->hash);
246
0
  free(a);
247
0
}
248
249
250
//
251
// 'cupsArrayDup()' - Duplicate an array.
252
//
253
254
cups_array_t *        // O - Duplicate array
255
cupsArrayDup(cups_array_t *a)   // I - Array
256
0
{
257
0
  cups_array_t  *da;      // Duplicate array
258
259
260
  // Range check input...
261
0
  if (!a)
262
0
    return (NULL);
263
264
  // Allocate memory for the array...
265
0
  da = calloc(1, sizeof(cups_array_t));
266
0
  if (!da)
267
0
    return (NULL);
268
269
0
  da->compare   = a->compare;
270
0
  da->copyfunc  = a->copyfunc;
271
0
  da->freefunc  = a->freefunc;
272
0
  da->data      = a->data;
273
0
  da->current   = a->current;
274
0
  da->insert    = a->insert;
275
0
  da->unique    = a->unique;
276
0
  da->num_saved = a->num_saved;
277
278
0
  memcpy(da->saved, a->saved, sizeof(a->saved));
279
280
0
  if (a->num_elements)
281
0
  {
282
    // Allocate memory for the elements...
283
0
    da->elements = malloc((size_t)a->num_elements * sizeof(void *));
284
0
    if (!da->elements)
285
0
    {
286
0
      free(da);
287
0
      return (NULL);
288
0
    }
289
290
    // Copy the element pointers...
291
0
    if (a->copyfunc)
292
0
    {
293
      // Use the copy function to make a copy of each element...
294
0
      size_t  i;      // Looping var
295
296
0
      for (i = 0; i < a->num_elements; i ++)
297
0
  da->elements[i] = (a->copyfunc)(a->elements[i], a->data);
298
0
    }
299
0
    else
300
0
    {
301
      // Just copy raw pointers...
302
0
      memcpy(da->elements, a->elements, (size_t)a->num_elements * sizeof(void *));
303
0
    }
304
305
0
    da->num_elements   = a->num_elements;
306
0
    da->alloc_elements = a->num_elements;
307
0
  }
308
309
  // Return the new array...
310
0
  return (da);
311
0
}
312
313
314
//
315
// 'cupsArrayFind()' - Find an element in an array.
316
//
317
318
void *          // O - Element found or @code NULL@
319
cupsArrayFind(cups_array_t *a,    // I - Array
320
              void         *e)    // I - Element
321
0
{
322
0
  size_t  current,    // Current element
323
0
    hash;     // Hash index
324
0
  int   diff;     // Difference
325
326
327
  // Range check input...
328
0
  if (!a || !a->num_elements || !e)
329
0
    return (NULL);
330
331
  // Look for a match...
332
0
  if (a->hash)
333
0
  {
334
0
    if ((hash = (*(a->hashfunc))(e, a->data)) >= a->hashsize)
335
0
    {
336
0
      current = a->current;
337
0
      hash    = SIZE_MAX;
338
0
    }
339
0
    else if ((current = a->hash[hash]) >= a->num_elements)
340
0
      current = a->current;
341
0
  }
342
0
  else
343
0
  {
344
0
    current = a->current;
345
0
    hash    = SIZE_MAX;
346
0
  }
347
348
0
  current = cups_array_find(a, e, current, &diff);
349
0
  if (!diff)
350
0
  {
351
    // Found a match!  If the array does not contain unique values, find the
352
    // first element that is the same...
353
0
    if (!a->unique && a->compare)
354
0
    {
355
      // The array is not unique, find the first match...
356
0
      while (current > 0 && !(*(a->compare))(e, a->elements[current - 1], a->data))
357
0
        current --;
358
0
    }
359
360
0
    a->current = current;
361
362
0
    if (hash < a->hashsize)
363
0
      a->hash[hash] = current;
364
365
0
    return (a->elements[current]);
366
0
  }
367
0
  else
368
0
  {
369
    // No match...
370
0
    a->current = SIZE_MAX;
371
372
0
    return (NULL);
373
0
  }
374
0
}
375
376
377
//
378
// 'cupsArrayGetCount()' - Get the number of elements in an array.
379
//
380
381
size_t          // O - Number of elements
382
cupsArrayGetCount(cups_array_t *a)  // I - Array
383
0
{
384
0
  return (a ? a->num_elements : 0);
385
0
}
386
387
388
//
389
// '_cupsArrayFree()' - Free a string in an array.
390
//
391
392
void
393
_cupsArrayFree(void *s,     // I - String to free
394
               void *data)    // I - Callback data (unused)
395
0
{
396
0
  (void)data;
397
398
0
  free(s);
399
0
}
400
401
402
//
403
// 'cupsArrayGetCurrent()' - Return the current element in an array.
404
//
405
// This function returns the current element in an array.  The current element
406
// is undefined until you call @link cupsArrayFind@, @link cupsArrayGetElement@,
407
// @link cupsArrayGetFirst@, or @link cupsArrayGetLast@.
408
//
409
410
void *          // O - Element
411
cupsArrayGetCurrent(cups_array_t *a)  // I - Array
412
0
{
413
  // Range check input...
414
0
  if (!a)
415
0
    return (NULL);
416
417
  // Return the current element...
418
0
  if (a->current < a->num_elements)
419
0
    return (a->elements[a->current]);
420
0
  else
421
0
    return (NULL);
422
0
}
423
424
425
//
426
// 'cupsArrayGetFirst()' - Get the first element in an array.
427
//
428
429
void *          // O - First element or `NULL` if the array is empty
430
cupsArrayGetFirst(cups_array_t *a)  // I - Array
431
0
{
432
0
  return (cupsArrayGetElement(a, 0));
433
0
}
434
435
436
//
437
// 'cupsArrayGetIndex()' - Get the index of the current element.
438
//
439
// This function returns the index of the current element or `SIZE_MAX` if
440
// there is no current element.  The current element is undefined until you call
441
// @link cupsArrayFind@, @link cupsArrayGetElement@, @link cupsArrayGetFirst@,
442
// or @link cupsArrayGetLast@.
443
//
444
445
size_t          // O - Index of the current element, starting at 0
446
cupsArrayGetIndex(cups_array_t *a)  // I - Array
447
0
{
448
0
  return (a ? a->current : SIZE_MAX);
449
0
}
450
451
452
//
453
// 'cupsArrayGetInsert()' - Get the index of the last added or inserted element.
454
//
455
// This function returns the index of the last added or inserted element or
456
// `SIZE_MAX` if no elements have been added or inserted.
457
//
458
459
size_t          // O - Index of the last added or inserted element, starting at 0
460
cupsArrayGetInsert(cups_array_t *a) // I - Array
461
0
{
462
0
  return (a ? a->insert : SIZE_MAX);
463
0
}
464
465
466
//
467
// 'cupsArrayGetElement()' - Get the N-th element in the array.
468
//
469
470
void *          // O - N-th element or `NULL`
471
cupsArrayGetElement(cups_array_t *a,  // I - Array
472
                    size_t       n) // I - Index into array, starting at 0
473
0
{
474
  // Range check input...
475
0
  if (!a || n >= a->num_elements)
476
0
    return (NULL);
477
478
0
  a->current = n;
479
480
0
  return (a->elements[n]);
481
0
}
482
483
484
//
485
// 'cupsArrayGetLast()' - Get the last element in the array.
486
//
487
488
void *          // O - Last element or`NULL` if the array is empty
489
cupsArrayGetLast(cups_array_t *a) // I - Array
490
0
{
491
  // Range check input...
492
0
  if (!a || a->num_elements == 0)
493
0
    return (NULL);
494
495
  // Return the last element...
496
0
  return (cupsArrayGetElement(a, a->num_elements - 1));
497
0
}
498
499
500
//
501
// 'cupsArrayGetNext()' - Get the next element in an array.
502
//
503
// This function returns the next element in an array.  The next element is
504
// undefined until you call @link cupsArrayFind@, @link cupsArrayGetElement@,
505
// @link cupsArrayGetFirst@, or @link cupsArrayGetLast@ to set the current
506
// element.
507
//
508
509
void *          // O - Next element or @code NULL@
510
cupsArrayGetNext(cups_array_t *a) // I - Array
511
0
{
512
  // Range check input...
513
0
  if (!a || a->num_elements == 0)
514
0
    return (NULL);
515
0
  else if (a->current == SIZE_MAX)
516
0
    return (cupsArrayGetElement(a, 0));
517
0
  else
518
0
    return (cupsArrayGetElement(a, a->current + 1));
519
0
}
520
521
522
//
523
// 'cupsArrayGetPrev()' - Get the previous element in an array.
524
//
525
// This function returns the previous element in an array.  The previous element
526
// is undefined until you call @link cupsArrayFind@, @link cupsArrayGetElement@,
527
// @link cupsArrayGetFirst@, or @link cupsArrayGetLast@ to set the current
528
// element.
529
//
530
531
void *          // O - Previous element or @code NULL@
532
cupsArrayGetPrev(cups_array_t *a) // I - Array
533
0
{
534
  // Range check input...
535
0
  if (!a || a->num_elements == 0 || a->current == 0 || a->current == SIZE_MAX)
536
0
    return (NULL);
537
0
  else
538
0
    return (cupsArrayGetElement(a, a->current - 1));
539
0
}
540
541
542
//
543
// 'cupsArrayGetUserData()' - Return the user data for an array.
544
//
545
546
void *          // O - User data
547
cupsArrayGetUserData(cups_array_t *a) // I - Array
548
0
{
549
0
  return (a ? a->data : NULL);
550
0
}
551
552
553
//
554
// 'cupsArrayInsert()' - Insert an element in an array.
555
//
556
// This function inserts an element in an array.  When inserting an element
557
// in a sorted array, non-unique elements are inserted at the beginning of the
558
// run of identical elements.  For unsorted arrays, the element is inserted at
559
// the beginning of the array.
560
//
561
562
bool          // O - `true` on success, `false` on failure
563
cupsArrayInsert(cups_array_t *a,  // I - Array
564
    void         *e)  // I - Element
565
0
{
566
  // Range check input...
567
0
  if (!a || !e)
568
0
    return (false);
569
570
  // Insert the element...
571
0
  return (cups_array_add(a, e, true));
572
0
}
573
574
575
//
576
// 'cupsArrayNew()' - Create a new array with callback functions.
577
//
578
// This function creates a new array with optional callback functions.  The
579
// comparison callback function ("f") is used to create a sorted array.  The
580
// function receives pointers to two elements and the user data pointer ("d").
581
// The user data pointer argument can safely be omitted when not required so
582
// functions like `strcmp` can be used for sorted string arrays.
583
//
584
// ```
585
// int // Return -1 if a < b, 0 if a == b, and 1 if a > b
586
// compare_cb(void *a, void *b, void *d)
587
// {
588
//   ... "a" and "b" are the elements, "d" is the user data pointer
589
// }
590
// ```
591
//
592
// The hash callback function ("hf") is used to implement cached lookups with
593
// the specified hash size ("hsize").  The function receives a pointer to an
594
// element and the user data pointer ("d") and returns an unsigned integer
595
// representing a hash into the array.  The hash value is of type `size_t` which
596
// provides at least 32-bits of resolution.
597
//
598
// ```
599
// size_t // Return hash value from 0 to (hashsize - 1)
600
// hash_cb(void *e, void *d)
601
// {
602
//   ... "e" is the element, "d" is the user data pointer
603
// }
604
// ```
605
//
606
// The copy callback function ("cf") is used to automatically copy/retain
607
// elements when added to the array or the array is copied with
608
// @link cupsArrayDup@.  The function receives a pointer to the element and the
609
// user data pointer ("d") and returns a new pointer that is stored in the array.
610
//
611
// ```
612
// void * // Return pointer to copied/retained element or NULL
613
// copy_cb(void *e, void *d)
614
// {
615
//   ... "e" is the element, "d" is the user data pointer
616
// }
617
// ```
618
//
619
// Finally, the free callback function ("cf") is used to automatically
620
// free/release elements when removed or the array is deleted.  The function
621
// receives a pointer to the element and the user data pointer ("d").
622
//
623
// ```
624
// void
625
// free_cb(void *e, void *d)
626
// {
627
//   ... "e" is the element, "d" is the user data pointer
628
// }
629
// ```
630
//
631
632
cups_array_t *        // O - Array
633
cupsArrayNew(cups_array_cb_t  f,  // I - Comparison callback function or `NULL` for an unsorted array
634
             void             *d, // I - User data or `NULL`
635
             cups_ahash_cb_t  hf, // I - Hash callback function or `NULL` for unhashed lookups
636
       size_t           hsize,  // I - Hash size (>= `0`)
637
       cups_acopy_cb_t  cf, // I - Copy callback function or `NULL` for none
638
       cups_afree_cb_t  ff) // I - Free callback function or `NULL` for none
639
0
{
640
0
  cups_array_t  *a;     // Array
641
642
643
  // Allocate memory for the array...
644
0
  if ((a = calloc(1, sizeof(cups_array_t))) == NULL)
645
0
    return (NULL);
646
647
0
  a->compare   = f;
648
0
  a->data      = d;
649
0
  a->current   = SIZE_MAX;
650
0
  a->insert    = SIZE_MAX;
651
0
  a->num_saved = 0;
652
0
  a->unique    = true;
653
654
0
  if (hsize > 0 && hf)
655
0
  {
656
0
    a->hashfunc  = hf;
657
0
    a->hashsize  = hsize;
658
0
    a->hash      = malloc((size_t)hsize * sizeof(size_t));
659
660
0
    if (!a->hash)
661
0
    {
662
0
      free(a);
663
0
      return (NULL);
664
0
    }
665
666
0
    memset(a->hash, -1, (size_t)hsize * sizeof(size_t));
667
0
  }
668
669
0
  a->copyfunc = cf;
670
0
  a->freefunc = ff;
671
672
0
  return (a);
673
0
}
674
675
676
//
677
// 'cupsArrayNewStrings()' - Create a new array of delimited strings.
678
//
679
// This function creates an array that holds zero or more strings.  The created
680
// array automatically manages copies of the strings passed and sorts them in
681
// ascending order using a case-sensitive comparison.  If the string pointer "s"
682
// is `NULL` or the empty string, no strings are added to the newly created
683
// array.
684
//
685
// Additional strings can be added using the @link cupsArrayAdd@ or
686
// @link cupsArrayAddStrings@ functions.
687
//
688
689
cups_array_t *        // O - Array
690
cupsArrayNewStrings(const char *s,  // I - Delimited strings or `NULL` to create an empty array
691
                    char       delim) // I - Delimiter character
692
0
{
693
0
  cups_array_t  *a;     // Array
694
695
696
0
  if ((a = cupsArrayNew(_cupsArrayStrcmp, NULL, NULL, 0, _cupsArrayStrdup, _cupsArrayFree)) != NULL)
697
0
    cupsArrayAddStrings(a, s, delim);
698
699
0
  return (a);
700
0
}
701
702
703
//
704
// 'cupsArrayRemove()' - Remove an element from an array.
705
//
706
// This function removes an element from an array.  If more than one element
707
// matches "e", only the first matching element is removed.
708
//
709
710
bool          // O - `true` on success, `false` on failure
711
cupsArrayRemove(cups_array_t *a,  // I - Array
712
                void         *e)  // I - Element
713
0
{
714
0
  size_t  i,      // Looping var
715
0
    current;    // Current element
716
0
  int   diff;     // Difference
717
718
719
  // Range check input...
720
0
  if (!a || a->num_elements == 0 || !e)
721
0
    return (false);
722
723
  // See if the element is in the array...
724
0
  current = cups_array_find(a, e, a->current, &diff);
725
0
  if (diff)
726
0
    return (false);
727
728
  // Yes, now remove it...
729
0
  a->num_elements --;
730
731
0
  if (a->freefunc)
732
0
    (a->freefunc)(a->elements[current], a->data);
733
734
0
  if (current < a->num_elements)
735
0
    memmove(a->elements + current, a->elements + current + 1, (a->num_elements - current) * sizeof(void *));
736
737
0
  if (current <= a->current)
738
0
  {
739
0
    if (a->current)
740
0
      a->current --;
741
0
    else
742
0
      a->current = SIZE_MAX;
743
0
  }
744
745
0
  if (current < a->insert)
746
0
    a->insert --;
747
0
  else if (current == a->insert)
748
0
    a->insert = SIZE_MAX;
749
750
0
  for (i = 0; i < a->num_saved; i ++)
751
0
  {
752
0
    if (current <= a->saved[i])
753
0
      a->saved[i] --;
754
0
  }
755
756
0
  if (a->num_elements <= 1)
757
0
    a->unique = true;
758
759
0
  return (true);
760
0
}
761
762
763
//
764
// 'cupsArrayRestore()' - Reset the current element to the last @link cupsArraySave@.
765
//
766
767
void *          // O - New current element
768
cupsArrayRestore(cups_array_t *a) // I - Array
769
0
{
770
  // Range check input...
771
0
  if (!a || a->num_saved == 0)
772
0
    return (NULL);
773
774
0
  a->num_saved --;
775
0
  a->current = a->saved[a->num_saved];
776
777
0
  if (a->current < a->num_elements)
778
0
    return (a->elements[a->current]);
779
0
  else
780
0
    return (NULL);
781
0
}
782
783
784
//
785
// 'cupsArraySave()' - Mark the current element for a later @link cupsArrayRestore@.
786
//
787
// The current element is undefined until you call @link cupsArrayFind@,
788
// @link cupsArrayGetElement@, @link cupsArrayGetFirst@, or
789
// @link cupsArrayGetLast@ to set the current element.
790
//
791
// The save/restore stack is guaranteed to be at least 32 elements deep.
792
//
793
794
bool          // O - `true` on success, `false` on failure
795
cupsArraySave(cups_array_t *a)    // I - Array
796
0
{
797
  // Range check input...
798
0
  if (!a || a->num_saved >= _CUPS_MAXSAVE)
799
0
    return (false);
800
801
0
  a->saved[a->num_saved] = a->current;
802
0
  a->num_saved ++;
803
804
0
  return (true);
805
0
}
806
807
808
//
809
// '_cupsArrayStrcmp()' - Compare two strings in an array.
810
//
811
812
int         // O - Result of comparison
813
_cupsArrayStrcmp(void *s,   // I - first string to compare
814
           void *t,   // I - second string to compare
815
                 void *data)    // I - Callback data (unused)
816
0
{
817
0
  (void)data;
818
819
0
  return (strcmp((const char *)s, (const char *)t));
820
0
}
821
822
823
//
824
// '_cupsArrayStrdup()' - Copy a string in an array.
825
//
826
827
void *          // O - Copy of string
828
_cupsArrayStrdup(void *s,   // I - String to copy
829
                 void *data)    // I - Callback data (unused)
830
0
{
831
0
  (void)data;
832
833
0
  return (strdup((const char *)s));
834
0
}
835
836
837
//
838
// 'cups_array_add()' - Insert or append an element to the array.
839
//
840
841
static bool       // O - `true` on success, `false` on failure
842
cups_array_add(cups_array_t *a,   // I - Array
843
               void         *e,   // I - Element to add
844
         bool         insert) // I - `true` = insert, `false` = append
845
0
{
846
0
  size_t  i,      // Looping var
847
0
    current;    // Current element
848
0
  int   diff;     // Comparison with current element
849
850
851
  // Verify we have room for the new element...
852
0
  if (a->num_elements >= a->alloc_elements)
853
0
  {
854
    // Allocate additional elements; start with 16 elements, then double the
855
    // size until 1024 elements, then add 1024 elements thereafter...
856
0
    void  **temp;     // New array elements
857
0
    size_t  count;      // New allocation count
858
859
0
    if (a->alloc_elements == 0)
860
0
      count = 16;
861
0
    else if (a->alloc_elements < 1024)
862
0
      count = a->alloc_elements * 2;
863
0
    else
864
0
      count = a->alloc_elements + 1024;
865
866
0
    if ((temp = realloc(a->elements, count * sizeof(void *))) == NULL)
867
0
      return (false);
868
869
0
    a->alloc_elements = count;
870
0
    a->elements       = temp;
871
0
  }
872
873
  // Find the insertion point for the new element; if there is no compare
874
  // function or elements, just add it to the beginning or end...
875
0
  if (!a->num_elements || !a->compare)
876
0
  {
877
    // No elements or comparison function, insert/append as needed...
878
0
    if (insert)
879
0
      current = 0;     // Insert at beginning
880
0
    else
881
0
      current = a->num_elements; // Append to the end
882
0
  }
883
0
  else
884
0
  {
885
    // Do a binary search for the insertion point...
886
0
    current = cups_array_find(a, e, a->insert, &diff);
887
888
0
    if (diff > 0)
889
0
    {
890
      // Insert after the current element...
891
0
      current ++;
892
0
    }
893
0
    else if (!diff)
894
0
    {
895
      // Compared equal, make sure we add to the begining or end of the current
896
      // run of equal elements...
897
0
      a->unique = false;
898
899
0
      if (insert)
900
0
      {
901
        // Insert at beginning of run...
902
0
  while (current > 0 && !(*(a->compare))(e, a->elements[current - 1], a->data))
903
0
          current --;
904
0
      }
905
0
      else
906
0
      {
907
        // Append at end of run...
908
0
  do
909
0
  {
910
0
          current ++;
911
0
  }
912
0
  while (current < a->num_elements && !(*(a->compare))(e, a->elements[current], a->data));
913
0
      }
914
0
    }
915
0
  }
916
917
  // Insert or append the element...
918
0
  if (current < a->num_elements)
919
0
  {
920
    // Shift other elements to the right...
921
0
    memmove(a->elements + current + 1, a->elements + current, (a->num_elements - current) * sizeof(void *));
922
923
0
    if (a->current >= current)
924
0
      a->current ++;
925
926
0
    for (i = 0; i < a->num_saved; i ++)
927
0
    {
928
0
      if (a->saved[i] >= current)
929
0
  a->saved[i] ++;
930
0
    }
931
0
  }
932
933
0
  if (a->copyfunc)
934
0
  {
935
0
    if ((a->elements[current] = (a->copyfunc)(e, a->data)) == NULL)
936
0
      return (false);
937
0
  }
938
0
  else
939
0
  {
940
0
    a->elements[current] = e;
941
0
  }
942
943
0
  a->num_elements ++;
944
0
  a->insert = current;
945
946
0
  return (true);
947
0
}
948
949
950
//
951
// 'cups_array_find()' - Find an element in the array.
952
//
953
954
static size_t       // O - Index of match
955
cups_array_find(cups_array_t *a,  // I - Array
956
          void         *e,  // I - Element
957
    size_t       prev,  // I - Previous index
958
    int          *rdiff)  // O - Difference of match
959
0
{
960
0
  size_t  left,     // Left side of search
961
0
    right,      // Right side of search
962
0
    current;    // Current element
963
0
  int   diff;     // Comparison with current element
964
965
966
0
  if (a->compare)
967
0
  {
968
    // Do a binary search for the element...
969
0
    if (prev < a->num_elements)
970
0
    {
971
      // Start search on either side of previous...
972
0
      if ((diff = (*(a->compare))(e, a->elements[prev], a->data)) == 0 || (diff < 0 && prev == 0) || (diff > 0 && prev == (a->num_elements - 1)))
973
0
      {
974
        // Exact or edge match, return it!
975
0
  *rdiff = diff;
976
977
0
  return (prev);
978
0
      }
979
0
      else if (diff < 0)
980
0
      {
981
        // Start with previous on right side...
982
0
  left  = 0;
983
0
  right = prev;
984
0
      }
985
0
      else
986
0
      {
987
        // Start wih previous on left side...
988
0
        left  = prev;
989
0
  right = a->num_elements - 1;
990
0
      }
991
0
    }
992
0
    else
993
0
    {
994
      // Start search in the middle...
995
0
      left  = 0;
996
0
      right = a->num_elements - 1;
997
0
    }
998
999
0
    do
1000
0
    {
1001
0
      current = (left + right) / 2;
1002
0
      diff    = (*(a->compare))(e, a->elements[current], a->data);
1003
1004
0
      if (diff == 0)
1005
0
  break;
1006
0
      else if (diff < 0)
1007
0
  right = current;
1008
0
      else
1009
0
  left = current;
1010
0
    }
1011
0
    while ((right - left) > 1);
1012
1013
0
    if (diff != 0)
1014
0
    {
1015
      // Check the last 1 or 2 elements...
1016
0
      if ((diff = (*(a->compare))(e, a->elements[left], a->data)) <= 0)
1017
0
      {
1018
0
        current = left;
1019
0
      }
1020
0
      else
1021
0
      {
1022
0
        diff    = (*(a->compare))(e, a->elements[right], a->data);
1023
0
        current = right;
1024
0
      }
1025
0
    }
1026
0
  }
1027
0
  else
1028
0
  {
1029
    // Do a linear pointer search...
1030
0
    diff = 1;
1031
1032
0
    for (current = 0; current < a->num_elements; current ++)
1033
0
    {
1034
0
      if (a->elements[current] == e)
1035
0
      {
1036
0
        diff = 0;
1037
0
        break;
1038
0
      }
1039
0
    }
1040
0
  }
1041
1042
  // Return the closest element and the difference...
1043
0
  *rdiff = diff;
1044
1045
0
  return (current);
1046
0
}