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