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