]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/options.c
Merge pull request #4788 from leoarnold/mailmap
[thirdparty/cups.git] / cups / options.c
CommitLineData
ef416fc2 1/*
f2d18633 2 * "$Id$"
ef416fc2 3 *
7e86f2f6 4 * Option routines for CUPS.
ef416fc2 5 *
7e86f2f6
MS
6 * Copyright 2007-2014 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
ef416fc2 8 *
7e86f2f6
MS
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/".
ef416fc2 14 *
7e86f2f6 15 * This file is subject to the Apple OS-Developed Software exception.
ef416fc2 16 */
17
18/*
19 * Include necessary headers...
20 */
21
7cf5915e 22#include "cups-private.h"
ef416fc2 23
24
426c6a59
MS
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
ef416fc2 34/*
35 * 'cupsAddOption()' - Add an option to an option array.
5a738aea
MS
36 *
37 * New option arrays can be initialized simply by passing 0 for the
38 * "num_options" parameter.
ef416fc2 39 */
40
5a738aea
MS
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 */
ef416fc2 45 cups_option_t **options) /* IO - Pointer to options */
46{
ef416fc2 47 cups_option_t *temp; /* Pointer to new option */
426c6a59
MS
48 int insert, /* Insertion point */
49 diff; /* Result of search */
ef416fc2 50
51
f11a948a 52 DEBUG_printf(("2cupsAddOption(name=\"%s\", value=\"%s\", num_options=%d, "
e07d4801 53 "options=%p)", name, value, num_options, options));
88f9aafc 54
20fbc903
MS
55 if (!name || !name[0] || !value || !options || num_options < 0)
56 {
f11a948a 57 DEBUG_printf(("3cupsAddOption: Returning %d", num_options));
ef416fc2 58 return (num_options);
20fbc903 59 }
ef416fc2 60
61 /*
62 * Look for an existing option with the same name...
63 */
64
426c6a59
MS
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);
ef416fc2 74
426c6a59
MS
75 if (diff > 0)
76 insert ++;
77 }
78
79 if (diff)
ef416fc2 80 {
81 /*
82 * No matching option name...
83 */
84
f11a948a 85 DEBUG_printf(("4cupsAddOption: New option inserted at index %d...",
426c6a59 86 insert));
20fbc903 87
ef416fc2 88 if (num_options == 0)
89 temp = (cups_option_t *)malloc(sizeof(cups_option_t));
90 else
7e86f2f6 91 temp = (cups_option_t *)realloc(*options, sizeof(cups_option_t) * (size_t)(num_options + 1));
ef416fc2 92
7e86f2f6 93 if (!temp)
20fbc903 94 {
f11a948a 95 DEBUG_puts("3cupsAddOption: Unable to expand option array, returning 0");
ef416fc2 96 return (0);
20fbc903 97 }
ef416fc2 98
426c6a59
MS
99 *options = temp;
100
101 if (insert < num_options)
102 {
f11a948a 103 DEBUG_printf(("4cupsAddOption: Shifting %d options...",
426c6a59 104 (int)(num_options - insert)));
7e86f2f6 105 memmove(temp + insert + 1, temp + insert, (size_t)(num_options - insert) * sizeof(cups_option_t));
426c6a59
MS
106 }
107
108 temp += insert;
2e4ff8af 109 temp->name = _cupsStrAlloc(name);
ef416fc2 110 num_options ++;
111 }
112 else
113 {
114 /*
115 * Match found; free the old value...
116 */
117
f11a948a 118 DEBUG_printf(("4cupsAddOption: Option already exists at index %d...",
426c6a59
MS
119 insert));
120
121 temp = *options + insert;
2e4ff8af 122 _cupsStrFree(temp->value);
ef416fc2 123 }
124
2e4ff8af 125 temp->value = _cupsStrAlloc(value);
ef416fc2 126
f11a948a 127 DEBUG_printf(("3cupsAddOption: Returning %d", num_options));
20fbc903 128
ef416fc2 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
e07d4801 145 DEBUG_printf(("cupsFreeOptions(num_options=%d, options=%p)", num_options,
20fbc903
MS
146 options));
147
148 if (num_options <= 0 || !options)
ef416fc2 149 return;
150
151 for (i = 0; i < num_options; i ++)
152 {
2e4ff8af
MS
153 _cupsStrFree(options[i].name);
154 _cupsStrFree(options[i].value);
ef416fc2 155 }
156
157 free(options);
158}
159
160
161/*
162 * 'cupsGetOption()' - Get an option value.
163 */
164
5a738aea 165const char * /* O - Option value or @code NULL@ */
ef416fc2 166cupsGetOption(const char *name, /* I - Name of option */
167 int num_options,/* I - Number of options */
168 cups_option_t *options) /* I - Options */
169{
426c6a59
MS
170 int diff, /* Result of comparison */
171 match; /* Matching index */
ef416fc2 172
173
e07d4801 174 DEBUG_printf(("2cupsGetOption(name=\"%s\", num_options=%d, options=%p)",
20fbc903
MS
175 name, num_options, options));
176
177 if (!name || num_options <= 0 || !options)
178 {
e07d4801 179 DEBUG_puts("3cupsGetOption: Returning NULL");
ef416fc2 180 return (NULL);
20fbc903 181 }
ef416fc2 182
426c6a59
MS
183 match = cups_find_option(name, num_options, options, -1, &diff);
184
185 if (!diff)
186 {
e07d4801 187 DEBUG_printf(("3cupsGetOption: Returning \"%s\"", options[match].value));
426c6a59
MS
188 return (options[match].value);
189 }
ef416fc2 190
e07d4801 191 DEBUG_puts("3cupsGetOption: Returning NULL");
ef416fc2 192 return (NULL);
193}
194
195
ef416fc2 196/*
b423cd4c 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
5a738aea
MS
202 * intact - use @code cupsParseOptions@ on the value to extract the
203 * collection attributes.
b423cd4c 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 */
5a738aea 215 *value, /* Pointer to value */
426c6a59 216 sep, /* Separator character */
5a738aea 217 quote; /* Quote character */
b423cd4c 218
219
e07d4801 220 DEBUG_printf(("cupsParseOptions(arg=\"%s\", num_options=%d, options=%p)",
20fbc903
MS
221 arg, num_options, options));
222
91c84a35
MS
223 /*
224 * Range check input...
225 */
226
227 if (!arg)
20fbc903 228 {
e07d4801 229 DEBUG_printf(("1cupsParseOptions: Returning %d", num_options));
91c84a35 230 return (num_options);
20fbc903 231 }
91c84a35
MS
232
233 if (!options || num_options < 0)
20fbc903 234 {
e07d4801 235 DEBUG_puts("1cupsParseOptions: Returning 0");
b423cd4c 236 return (0);
20fbc903 237 }
b423cd4c 238
239 /*
240 * Make a copy of the argument string and then divide it up...
241 */
242
91c84a35 243 if ((copyarg = strdup(arg)) == NULL)
20fbc903 244 {
e07d4801
MS
245 DEBUG_puts("1cupsParseOptions: Unable to copy arg string");
246 DEBUG_printf(("1cupsParseOptions: Returning %d", num_options));
91c84a35 247 return (num_options);
20fbc903 248 }
91c84a35 249
ee571f26
MS
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;
b423cd4c 266
267 /*
268 * Skip leading spaces...
269 */
270
7cf5915e 271 while (_cups_isspace(*ptr))
b423cd4c 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;
7cf5915e 285 while (!strchr("\f\n\r\t\v =", *ptr) && *ptr)
b423cd4c 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
7cf5915e 299 while (_cups_isspace(*ptr))
b423cd4c 300 *ptr++ = '\0';
301
426c6a59
MS
302 if ((sep = *ptr) == '=')
303 *ptr++ = '\0';
304
e07d4801 305 DEBUG_printf(("2cupsParseOptions: name=\"%s\"", name));
20fbc903 306
426c6a59 307 if (sep != '=')
b423cd4c 308 {
309 /*
5a738aea 310 * Boolean option...
b423cd4c 311 */
312
88f9aafc 313 if (!_cups_strncasecmp(name, "no", 2))
b423cd4c 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
426c6a59 326 value = ptr;
b423cd4c 327
7cf5915e 328 while (*ptr && !_cups_isspace(*ptr))
b423cd4c 329 {
20fbc903
MS
330 if (*ptr == ',')
331 ptr ++;
332 else if (*ptr == '\'' || *ptr == '\"')
b423cd4c 333 {
20fbc903
MS
334 /*
335 * Quoted string constant...
336 */
b423cd4c 337
20fbc903
MS
338 quote = *ptr;
339 _cups_strcpy(ptr, ptr + 1);
b423cd4c 340
20fbc903
MS
341 while (*ptr != quote && *ptr)
342 {
343 if (*ptr == '\\' && ptr[1])
344 _cups_strcpy(ptr, ptr + 1);
345
346 ptr ++;
347 }
b423cd4c 348
20fbc903
MS
349 if (*ptr)
350 _cups_strcpy(ptr, ptr + 1);
351 }
352 else if (*ptr == '{')
353 {
354 /*
355 * Collection value...
356 */
b423cd4c 357
20fbc903 358 int depth;
b423cd4c 359
20fbc903 360 for (depth = 0; *ptr; ptr ++)
b423cd4c 361 {
20fbc903
MS
362 if (*ptr == '{')
363 depth ++;
364 else if (*ptr == '}')
b423cd4c 365 {
20fbc903
MS
366 depth --;
367 if (!depth)
368 {
369 ptr ++;
b423cd4c 370 break;
20fbc903 371 }
b423cd4c 372 }
20fbc903
MS
373 else if (*ptr == '\\' && ptr[1])
374 _cups_strcpy(ptr, ptr + 1);
375 }
376 }
377 else
b423cd4c 378 {
20fbc903
MS
379 /*
380 * Normal space-delimited string...
381 */
b423cd4c 382
7cf5915e 383 while (*ptr && !_cups_isspace(*ptr))
20fbc903
MS
384 {
385 if (*ptr == '\\' && ptr[1])
386 _cups_strcpy(ptr, ptr + 1);
387
388 ptr ++;
389 }
b423cd4c 390 }
391 }
392
20fbc903
MS
393 if (*ptr != '\0')
394 *ptr++ = '\0';
395
e07d4801 396 DEBUG_printf(("2cupsParseOptions: value=\"%s\"", value));
20fbc903 397
b423cd4c 398 /*
399 * Skip trailing whitespace...
400 */
401
7cf5915e 402 while (_cups_isspace(*ptr))
20fbc903 403 ptr ++;
b423cd4c 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
e07d4801 419 DEBUG_printf(("1cupsParseOptions: Returning %d", num_options));
20fbc903 420
b423cd4c 421 return (num_options);
422}
423
424
425/*
f7deaa1a 426 * 'cupsRemoveOption()' - Remove an option from an option array.
b423cd4c 427 *
f3c17241 428 * @since CUPS 1.2/OS X 10.5@
b423cd4c 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
f11a948a 441 DEBUG_printf(("2cupsRemoveOption(name=\"%s\", num_options=%d, options=%p)",
20fbc903
MS
442 name, num_options, options));
443
b423cd4c 444 /*
445 * Range check input...
446 */
447
448 if (!name || num_options < 1 || !options)
20fbc903 449 {
f11a948a 450 DEBUG_printf(("3cupsRemoveOption: Returning %d", num_options));
b423cd4c 451 return (num_options);
20fbc903 452 }
b423cd4c 453
454 /*
455 * Loop for the option...
456 */
457
458 for (i = num_options, option = *options; i > 0; i --, option ++)
88f9aafc 459 if (!_cups_strcasecmp(name, option->name))
b423cd4c 460 break;
461
462 if (i)
463 {
464 /*
465 * Remove this option from the array...
466 */
467
f11a948a 468 DEBUG_puts("4cupsRemoveOption: Found option, removing it...");
20fbc903 469
b423cd4c 470 num_options --;
471 i --;
472
2e4ff8af
MS
473 _cupsStrFree(option->name);
474 _cupsStrFree(option->value);
b423cd4c 475
476 if (i > 0)
7e86f2f6 477 memmove(option, option + 1, (size_t)i * sizeof(cups_option_t));
b423cd4c 478 }
479
480 /*
481 * Return the new number of options...
482 */
483
f11a948a 484 DEBUG_printf(("3cupsRemoveOption: Returning %d", num_options));
b423cd4c 485 return (num_options);
486}
487
488
f8b3a85b
MS
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 {
7cf5915e 527 while (_cups_isspace(*device_id))
f8b3a85b
MS
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
7cf5915e 540 while (ptr > key && _cups_isspace(ptr[-1]))
f8b3a85b
MS
541 ptr --;
542
543 *ptr = '\0';
544 device_id ++;
545
7cf5915e 546 while (_cups_isspace(*device_id))
f8b3a85b
MS
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
7cf5915e 559 while (ptr > value && _cups_isspace(ptr[-1]))
f8b3a85b
MS
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
09a101d6 572/*
426c6a59
MS
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{
88f9aafc 580 return (_cups_strcasecmp(a->name, b->name));
426c6a59
MS
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
e07d4801
MS
603 DEBUG_printf(("7cups_find_option(name=\"%s\", num_options=%d, options=%p, "
604 "prev=%d, rdiff=%p)", name, num_options, options, prev,
426c6a59
MS
605 rdiff));
606
607#ifdef DEBUG
608 for (left = 0; left < num_options; left ++)
e07d4801 609 DEBUG_printf(("9cups_find_option: options[%d].name=\"%s\", .value=\"%s\"",
426c6a59
MS
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/*
f2d18633 697 * End of "$Id$".
ef416fc2 698 */