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