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