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