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