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