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