• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
cupsAddIntegerOption(const char * name,int value,int num_options,cups_option_t ** 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 */
cupsAddOption(const char * name,const char * value,int num_options,cups_option_t ** 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
cupsFreeOptions(int num_options,cups_option_t * options)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@ */
cupsGetIntegerOption(const char * name,int num_options,cups_option_t * options)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@ */
cupsGetOption(const char * name,int num_options,cups_option_t * options)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 */
cupsParseOptions(const char * arg,int num_options,cups_option_t ** options)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 */
cupsRemoveOption(const char * name,int num_options,cups_option_t ** 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 */
_cupsGet1284Values(const char * device_id,cups_option_t ** values)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 */
cups_compare_options(cups_option_t * a,cups_option_t * b)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 */
cups_find_option(const char * name,int num_options,cups_option_t * options,int prev,int * rdiff)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