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