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 */
cupsAddOption(const char * name,const char * value,int num_options,cups_option_t ** 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
cupsFreeOptions(int num_options,cups_option_t * options)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@ */
cupsGetOption(const char * name,int num_options,cups_option_t * options)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 */
cupsParseOptions(const char * arg,int num_options,cups_option_t ** options)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 */
cupsRemoveOption(const char * name,int num_options,cups_option_t ** 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 */
_cupsGet1284Values(const char * device_id,cups_option_t ** values)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 */
cups_compare_options(cups_option_t * a,cups_option_t * b)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 */
cups_find_option(const char * name,int num_options,cups_option_t * options,int prev,int * rdiff)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