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