1/* 2 * "$Id: options.c 11093 2013-07-03 20:48:42Z msweet $" 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 40static int cups_compare_options(cups_option_t *a, cups_option_t *b); 41static 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 52int /* O - Number of options */ 53cupsAddOption(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 150void 151cupsFreeOptions( 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 178const char * /* O - Option value or @code NULL@ */ 179cupsGetOption(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 219int /* O - Number of options found */ 220cupsParseOptions( 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 444int /* O - New number of options */ 445cupsRemoveOption( 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 509int /* 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 589static int /* O - Result of comparison */ 590cups_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 601static int /* O - Index of match */ 602cups_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: options.c 11093 2013-07-03 20:48:42Z msweet $". 711 */ 712