Home | History | Annotate | Download | only in cups
      1 /*
      2  * Option 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-private.h"
     21 
     22 
     23 /*
     24  * Local functions...
     25  */
     26 
     27 static int	cups_compare_options(cups_option_t *a, cups_option_t *b);
     28 static int	cups_find_option(const char *name, int num_options,
     29 	                         cups_option_t *option, int prev, int *rdiff);
     30 
     31 
     32 /*
     33  * 'cupsAddOption()' - Add an option to an option array.
     34  *
     35  * New option arrays can be initialized simply by passing 0 for the
     36  * "num_options" parameter.
     37  */
     38 
     39 int					/* O  - Number of options */
     40 cupsAddOption(const char    *name,	/* I  - Name of option */
     41               const char    *value,	/* I  - Value of option */
     42 	      int           num_options,/* I  - Number of options */
     43               cups_option_t **options)	/* IO - Pointer to options */
     44 {
     45   cups_option_t	*temp;			/* Pointer to new option */
     46   int		insert,			/* Insertion point */
     47 		diff;			/* Result of search */
     48 
     49 
     50   DEBUG_printf(("2cupsAddOption(name=\"%s\", value=\"%s\", num_options=%d, options=%p)", name, value, num_options, (void *)options));
     51 
     52   if (!name || !name[0] || !value || !options || num_options < 0)
     53   {
     54     DEBUG_printf(("3cupsAddOption: Returning %d", num_options));
     55     return (num_options);
     56   }
     57 
     58  /*
     59   * Look for an existing option with the same name...
     60   */
     61 
     62   if (num_options == 0)
     63   {
     64     insert = 0;
     65     diff   = 1;
     66   }
     67   else
     68   {
     69     insert = cups_find_option(name, num_options, *options, num_options - 1,
     70                               &diff);
     71 
     72     if (diff > 0)
     73       insert ++;
     74   }
     75 
     76   if (diff)
     77   {
     78    /*
     79     * No matching option name...
     80     */
     81 
     82     DEBUG_printf(("4cupsAddOption: New option inserted at index %d...",
     83                   insert));
     84 
     85     if (num_options == 0)
     86       temp = (cups_option_t *)malloc(sizeof(cups_option_t));
     87     else
     88       temp = (cups_option_t *)realloc(*options, sizeof(cups_option_t) * (size_t)(num_options + 1));
     89 
     90     if (!temp)
     91     {
     92       DEBUG_puts("3cupsAddOption: Unable to expand option array, returning 0");
     93       return (0);
     94     }
     95 
     96     *options = temp;
     97 
     98     if (insert < num_options)
     99     {
    100       DEBUG_printf(("4cupsAddOption: Shifting %d options...",
    101                     (int)(num_options - insert)));
    102       memmove(temp + insert + 1, temp + insert, (size_t)(num_options - insert) * sizeof(cups_option_t));
    103     }
    104 
    105     temp        += insert;
    106     temp->name  = _cupsStrAlloc(name);
    107     num_options ++;
    108   }
    109   else
    110   {
    111    /*
    112     * Match found; free the old value...
    113     */
    114 
    115     DEBUG_printf(("4cupsAddOption: Option already exists at index %d...",
    116                   insert));
    117 
    118     temp = *options + insert;
    119     _cupsStrFree(temp->value);
    120   }
    121 
    122   temp->value = _cupsStrAlloc(value);
    123 
    124   DEBUG_printf(("3cupsAddOption: Returning %d", num_options));
    125 
    126   return (num_options);
    127 }
    128 
    129 
    130 /*
    131  * 'cupsFreeOptions()' - Free all memory used by options.
    132  */
    133 
    134 void
    135 cupsFreeOptions(
    136     int           num_options,		/* I - Number of options */
    137     cups_option_t *options)		/* I - Pointer to options */
    138 {
    139   int	i;				/* Looping var */
    140 
    141 
    142   DEBUG_printf(("cupsFreeOptions(num_options=%d, options=%p)", num_options, (void *)options));
    143 
    144   if (num_options <= 0 || !options)
    145     return;
    146 
    147   for (i = 0; i < num_options; i ++)
    148   {
    149     _cupsStrFree(options[i].name);
    150     _cupsStrFree(options[i].value);
    151   }
    152 
    153   free(options);
    154 }
    155 
    156 
    157 /*
    158  * 'cupsGetOption()' - Get an option value.
    159  */
    160 
    161 const char *				/* O - Option value or @code NULL@ */
    162 cupsGetOption(const char    *name,	/* I - Name of option */
    163               int           num_options,/* I - Number of options */
    164               cups_option_t *options)	/* I - Options */
    165 {
    166   int	diff,				/* Result of comparison */
    167 	match;				/* Matching index */
    168 
    169 
    170   DEBUG_printf(("2cupsGetOption(name=\"%s\", num_options=%d, options=%p)", name, num_options, (void *)options));
    171 
    172   if (!name || num_options <= 0 || !options)
    173   {
    174     DEBUG_puts("3cupsGetOption: Returning NULL");
    175     return (NULL);
    176   }
    177 
    178   match = cups_find_option(name, num_options, options, -1, &diff);
    179 
    180   if (!diff)
    181   {
    182     DEBUG_printf(("3cupsGetOption: Returning \"%s\"", options[match].value));
    183     return (options[match].value);
    184   }
    185 
    186   DEBUG_puts("3cupsGetOption: Returning NULL");
    187   return (NULL);
    188 }
    189 
    190 
    191 /*
    192  * 'cupsParseOptions()' - Parse options from a command-line argument.
    193  *
    194  * This function converts space-delimited name/value pairs according
    195  * to the PAPI text option ABNF specification. Collection values
    196  * ("name={a=... b=... c=...}") are stored with the curley brackets
    197  * intact - use @code cupsParseOptions@ on the value to extract the
    198  * collection attributes.
    199  */
    200 
    201 int					/* O - Number of options found */
    202 cupsParseOptions(
    203     const char    *arg,			/* I - Argument to parse */
    204     int           num_options,		/* I - Number of options */
    205     cups_option_t **options)		/* O - Options found */
    206 {
    207   char	*copyarg,			/* Copy of input string */
    208 	*ptr,				/* Pointer into string */
    209 	*name,				/* Pointer to name */
    210 	*value,				/* Pointer to value */
    211 	sep,				/* Separator character */
    212 	quote;				/* Quote character */
    213 
    214 
    215   DEBUG_printf(("cupsParseOptions(arg=\"%s\", num_options=%d, options=%p)", arg, num_options, (void *)options));
    216 
    217  /*
    218   * Range check input...
    219   */
    220 
    221   if (!arg)
    222   {
    223     DEBUG_printf(("1cupsParseOptions: Returning %d", num_options));
    224     return (num_options);
    225   }
    226 
    227   if (!options || num_options < 0)
    228   {
    229     DEBUG_puts("1cupsParseOptions: Returning 0");
    230     return (0);
    231   }
    232 
    233  /*
    234   * Make a copy of the argument string and then divide it up...
    235   */
    236 
    237   if ((copyarg = strdup(arg)) == NULL)
    238   {
    239     DEBUG_puts("1cupsParseOptions: Unable to copy arg string");
    240     DEBUG_printf(("1cupsParseOptions: Returning %d", num_options));
    241     return (num_options);
    242   }
    243 
    244   if (*copyarg == '{')
    245   {
    246    /*
    247     * Remove surrounding {} so we can parse "{name=value ... name=value}"...
    248     */
    249 
    250     if ((ptr = copyarg + strlen(copyarg) - 1) > copyarg && *ptr == '}')
    251     {
    252       *ptr = '\0';
    253       ptr  = copyarg + 1;
    254     }
    255     else
    256       ptr = copyarg;
    257   }
    258   else
    259     ptr = copyarg;
    260 
    261  /*
    262   * Skip leading spaces...
    263   */
    264 
    265   while (_cups_isspace(*ptr))
    266     ptr ++;
    267 
    268  /*
    269   * Loop through the string...
    270   */
    271 
    272   while (*ptr != '\0')
    273   {
    274    /*
    275     * Get the name up to a SPACE, =, or end-of-string...
    276     */
    277 
    278     name = ptr;
    279     while (!strchr("\f\n\r\t\v =", *ptr) && *ptr)
    280       ptr ++;
    281 
    282    /*
    283     * Avoid an empty name...
    284     */
    285 
    286     if (ptr == name)
    287       break;
    288 
    289    /*
    290     * Skip trailing spaces...
    291     */
    292 
    293     while (_cups_isspace(*ptr))
    294       *ptr++ = '\0';
    295 
    296     if ((sep = *ptr) == '=')
    297       *ptr++ = '\0';
    298 
    299     DEBUG_printf(("2cupsParseOptions: name=\"%s\"", name));
    300 
    301     if (sep != '=')
    302     {
    303      /*
    304       * Boolean option...
    305       */
    306 
    307       if (!_cups_strncasecmp(name, "no", 2))
    308         num_options = cupsAddOption(name + 2, "false", num_options,
    309 	                            options);
    310       else
    311         num_options = cupsAddOption(name, "true", num_options, options);
    312 
    313       continue;
    314     }
    315 
    316    /*
    317     * Remove = and parse the value...
    318     */
    319 
    320     value = ptr;
    321 
    322     while (*ptr && !_cups_isspace(*ptr))
    323     {
    324       if (*ptr == ',')
    325         ptr ++;
    326       else if (*ptr == '\'' || *ptr == '\"')
    327       {
    328        /*
    329 	* Quoted string constant...
    330 	*/
    331 
    332 	quote = *ptr;
    333 	_cups_strcpy(ptr, ptr + 1);
    334 
    335 	while (*ptr != quote && *ptr)
    336 	{
    337 	  if (*ptr == '\\' && ptr[1])
    338 	    _cups_strcpy(ptr, ptr + 1);
    339 
    340 	  ptr ++;
    341 	}
    342 
    343 	if (*ptr)
    344 	  _cups_strcpy(ptr, ptr + 1);
    345       }
    346       else if (*ptr == '{')
    347       {
    348        /*
    349 	* Collection value...
    350 	*/
    351 
    352 	int depth;
    353 
    354 	for (depth = 0; *ptr; ptr ++)
    355 	{
    356 	  if (*ptr == '{')
    357 	    depth ++;
    358 	  else if (*ptr == '}')
    359 	  {
    360 	    depth --;
    361 	    if (!depth)
    362 	    {
    363 	      ptr ++;
    364 	      break;
    365 	    }
    366 	  }
    367 	  else if (*ptr == '\\' && ptr[1])
    368 	    _cups_strcpy(ptr, ptr + 1);
    369 	}
    370       }
    371       else
    372       {
    373        /*
    374 	* Normal space-delimited string...
    375 	*/
    376 
    377 	while (*ptr && !_cups_isspace(*ptr))
    378 	{
    379 	  if (*ptr == '\\' && ptr[1])
    380 	    _cups_strcpy(ptr, ptr + 1);
    381 
    382 	  ptr ++;
    383 	}
    384       }
    385     }
    386 
    387     if (*ptr != '\0')
    388       *ptr++ = '\0';
    389 
    390     DEBUG_printf(("2cupsParseOptions: value=\"%s\"", value));
    391 
    392    /*
    393     * Skip trailing whitespace...
    394     */
    395 
    396     while (_cups_isspace(*ptr))
    397       ptr ++;
    398 
    399    /*
    400     * Add the string value...
    401     */
    402 
    403     num_options = cupsAddOption(name, value, num_options, options);
    404   }
    405 
    406  /*
    407   * Free the copy of the argument we made and return the number of options
    408   * found.
    409   */
    410 
    411   free(copyarg);
    412 
    413   DEBUG_printf(("1cupsParseOptions: Returning %d", num_options));
    414 
    415   return (num_options);
    416 }
    417 
    418 
    419 /*
    420  * 'cupsRemoveOption()' - Remove an option from an option array.
    421  *
    422  * @since CUPS 1.2/macOS 10.5@
    423  */
    424 
    425 int					/* O  - New number of options */
    426 cupsRemoveOption(
    427     const char    *name,		/* I  - Option name */
    428     int           num_options,		/* I  - Current number of options */
    429     cups_option_t **options)		/* IO - Options */
    430 {
    431   int		i;			/* Looping var */
    432   cups_option_t	*option;		/* Current option */
    433 
    434 
    435   DEBUG_printf(("2cupsRemoveOption(name=\"%s\", num_options=%d, options=%p)", name, num_options, (void *)options));
    436 
    437  /*
    438   * Range check input...
    439   */
    440 
    441   if (!name || num_options < 1 || !options)
    442   {
    443     DEBUG_printf(("3cupsRemoveOption: Returning %d", num_options));
    444     return (num_options);
    445   }
    446 
    447  /*
    448   * Loop for the option...
    449   */
    450 
    451   for (i = num_options, option = *options; i > 0; i --, option ++)
    452     if (!_cups_strcasecmp(name, option->name))
    453       break;
    454 
    455   if (i)
    456   {
    457    /*
    458     * Remove this option from the array...
    459     */
    460 
    461     DEBUG_puts("4cupsRemoveOption: Found option, removing it...");
    462 
    463     num_options --;
    464     i --;
    465 
    466     _cupsStrFree(option->name);
    467     _cupsStrFree(option->value);
    468 
    469     if (i > 0)
    470       memmove(option, option + 1, (size_t)i * sizeof(cups_option_t));
    471   }
    472 
    473  /*
    474   * Return the new number of options...
    475   */
    476 
    477   DEBUG_printf(("3cupsRemoveOption: Returning %d", num_options));
    478   return (num_options);
    479 }
    480 
    481 
    482 /*
    483  * '_cupsGet1284Values()' - Get 1284 device ID keys and values.
    484  *
    485  * The returned dictionary is a CUPS option array that can be queried with
    486  * cupsGetOption and freed with cupsFreeOptions.
    487  */
    488 
    489 int					/* O - Number of key/value pairs */
    490 _cupsGet1284Values(
    491     const char *device_id,		/* I - IEEE-1284 device ID string */
    492     cups_option_t **values)		/* O - Array of key/value pairs */
    493 {
    494   int		num_values;		/* Number of values */
    495   char		key[256],		/* Key string */
    496 		value[256],		/* Value string */
    497 		*ptr;			/* Pointer into key/value */
    498 
    499 
    500  /*
    501   * Range check input...
    502   */
    503 
    504   if (values)
    505     *values = NULL;
    506 
    507   if (!device_id || !values)
    508     return (0);
    509 
    510  /*
    511   * Parse the 1284 device ID value into keys and values.  The format is
    512   * repeating sequences of:
    513   *
    514   *   [whitespace]key:value[whitespace];
    515   */
    516 
    517   num_values = 0;
    518   while (*device_id)
    519   {
    520     while (_cups_isspace(*device_id))
    521       device_id ++;
    522 
    523     if (!*device_id)
    524       break;
    525 
    526     for (ptr = key; *device_id && *device_id != ':'; device_id ++)
    527       if (ptr < (key + sizeof(key) - 1))
    528         *ptr++ = *device_id;
    529 
    530     if (!*device_id)
    531       break;
    532 
    533     while (ptr > key && _cups_isspace(ptr[-1]))
    534       ptr --;
    535 
    536     *ptr = '\0';
    537     device_id ++;
    538 
    539     while (_cups_isspace(*device_id))
    540       device_id ++;
    541 
    542     if (!*device_id)
    543       break;
    544 
    545     for (ptr = value; *device_id && *device_id != ';'; device_id ++)
    546       if (ptr < (value + sizeof(value) - 1))
    547         *ptr++ = *device_id;
    548 
    549     if (!*device_id)
    550       break;
    551 
    552     while (ptr > value && _cups_isspace(ptr[-1]))
    553       ptr --;
    554 
    555     *ptr = '\0';
    556     device_id ++;
    557 
    558     num_values = cupsAddOption(key, value, num_values, values);
    559   }
    560 
    561   return (num_values);
    562 }
    563 
    564 
    565 /*
    566  * 'cups_compare_options()' - Compare two options.
    567  */
    568 
    569 static int				/* O - Result of comparison */
    570 cups_compare_options(cups_option_t *a,	/* I - First option */
    571 		     cups_option_t *b)	/* I - Second option */
    572 {
    573   return (_cups_strcasecmp(a->name, b->name));
    574 }
    575 
    576 
    577 /*
    578  * 'cups_find_option()' - Find an option using a binary search.
    579  */
    580 
    581 static int				/* O - Index of match */
    582 cups_find_option(
    583     const char    *name,		/* I - Option name */
    584     int           num_options,		/* I - Number of options */
    585     cups_option_t *options,		/* I - Options */
    586     int           prev,			/* I - Previous index */
    587     int           *rdiff)		/* O - Difference of match */
    588 {
    589   int		left,			/* Low mark for binary search */
    590 		right,			/* High mark for binary search */
    591 		current,		/* Current index */
    592 		diff;			/* Result of comparison */
    593   cups_option_t	key;			/* Search key */
    594 
    595 
    596   DEBUG_printf(("7cups_find_option(name=\"%s\", num_options=%d, options=%p, prev=%d, rdiff=%p)", name, num_options, (void *)options, prev, (void *)rdiff));
    597 
    598 #ifdef DEBUG
    599   for (left = 0; left < num_options; left ++)
    600     DEBUG_printf(("9cups_find_option: options[%d].name=\"%s\", .value=\"%s\"",
    601                   left, options[left].name, options[left].value));
    602 #endif /* DEBUG */
    603 
    604   key.name = (char *)name;
    605 
    606   if (prev >= 0)
    607   {
    608    /*
    609     * Start search on either side of previous...
    610     */
    611 
    612     if ((diff = cups_compare_options(&key, options + prev)) == 0 ||
    613         (diff < 0 && prev == 0) ||
    614 	(diff > 0 && prev == (num_options - 1)))
    615     {
    616       *rdiff = diff;
    617       return (prev);
    618     }
    619     else if (diff < 0)
    620     {
    621      /*
    622       * Start with previous on right side...
    623       */
    624 
    625       left  = 0;
    626       right = prev;
    627     }
    628     else
    629     {
    630      /*
    631       * Start wih previous on left side...
    632       */
    633 
    634       left  = prev;
    635       right = num_options - 1;
    636     }
    637   }
    638   else
    639   {
    640    /*
    641     * Start search in the middle...
    642     */
    643 
    644     left  = 0;
    645     right = num_options - 1;
    646   }
    647 
    648   do
    649   {
    650     current = (left + right) / 2;
    651     diff    = cups_compare_options(&key, options + current);
    652 
    653     if (diff == 0)
    654       break;
    655     else if (diff < 0)
    656       right = current;
    657     else
    658       left = current;
    659   }
    660   while ((right - left) > 1);
    661 
    662   if (diff != 0)
    663   {
    664    /*
    665     * Check the last 1 or 2 elements...
    666     */
    667 
    668     if ((diff = cups_compare_options(&key, options + left)) <= 0)
    669       current = left;
    670     else
    671     {
    672       diff    = cups_compare_options(&key, options + right);
    673       current = right;
    674     }
    675   }
    676 
    677  /*
    678   * Return the closest destination and the difference...
    679   */
    680 
    681   *rdiff = diff;
    682 
    683   return (current);
    684 }
    685