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