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