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