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