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