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