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