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