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