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