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