1/* 2 * "$Id: options.c 11560 2014-02-06 20:10:19Z msweet $" 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 29static int cups_compare_options(cups_option_t *a, cups_option_t *b); 30static 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 41int /* O - Number of options */ 42cupsAddOption(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 137void 138cupsFreeOptions( 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 165const char * /* O - Option value or @code NULL@ */ 166cupsGetOption(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 206int /* O - Number of options found */ 207cupsParseOptions( 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 431int /* O - New number of options */ 432cupsRemoveOption( 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 496int /* 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 576static int /* O - Result of comparison */ 577cups_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 588static int /* O - Index of match */ 589cups_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: options.c 11560 2014-02-06 20:10:19Z msweet $". 698 */ 699