1 /* 2 * Option marking routines for CUPS. 3 * 4 * Copyright 2007-2015 by Apple Inc. 5 * Copyright 1997-2007 by Easy Software Products, all rights reserved. 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 * PostScript is a trademark of Adobe Systems, Inc. 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 #include "ppd-private.h" 24 25 26 /* 27 * Local functions... 28 */ 29 30 #ifdef DEBUG 31 static void ppd_debug_marked(ppd_file_t *ppd, const char *title); 32 #else 33 # define ppd_debug_marked(ppd,title) 34 #endif /* DEBUG */ 35 static void ppd_defaults(ppd_file_t *ppd, ppd_group_t *g); 36 static void ppd_mark_choices(ppd_file_t *ppd, const char *s); 37 static void ppd_mark_option(ppd_file_t *ppd, const char *option, 38 const char *choice); 39 40 41 /* 42 * 'cupsMarkOptions()' - Mark command-line options in a PPD file. 43 * 44 * This function maps the IPP "finishings", "media", "mirror", 45 * "multiple-document-handling", "output-bin", "print-color-mode", 46 * "print-quality", "printer-resolution", and "sides" attributes to their 47 * corresponding PPD options and choices. 48 */ 49 50 int /* O - 1 if conflicts exist, 0 otherwise */ 51 cupsMarkOptions( 52 ppd_file_t *ppd, /* I - PPD file */ 53 int num_options, /* I - Number of options */ 54 cups_option_t *options) /* I - Options */ 55 { 56 int i, j; /* Looping vars */ 57 char *ptr, /* Pointer into string */ 58 s[255]; /* Temporary string */ 59 const char *val, /* Pointer into value */ 60 *media, /* media option */ 61 *output_bin, /* output-bin option */ 62 *page_size, /* PageSize option */ 63 *ppd_keyword, /* PPD keyword */ 64 *print_color_mode, /* print-color-mode option */ 65 *print_quality, /* print-quality option */ 66 *sides; /* sides option */ 67 cups_option_t *optptr; /* Current option */ 68 ppd_attr_t *attr; /* PPD attribute */ 69 _ppd_cache_t *cache; /* PPD cache and mapping data */ 70 71 72 /* 73 * Check arguments... 74 */ 75 76 if (!ppd || num_options <= 0 || !options) 77 return (0); 78 79 ppd_debug_marked(ppd, "Before..."); 80 81 /* 82 * Do special handling for finishings, media, output-bin, output-mode, 83 * print-color-mode, print-quality, and PageSize... 84 */ 85 86 media = cupsGetOption("media", num_options, options); 87 output_bin = cupsGetOption("output-bin", num_options, options); 88 page_size = cupsGetOption("PageSize", num_options, options); 89 print_quality = cupsGetOption("print-quality", num_options, options); 90 sides = cupsGetOption("sides", num_options, options); 91 92 if ((print_color_mode = cupsGetOption("print-color-mode", num_options, 93 options)) == NULL) 94 print_color_mode = cupsGetOption("output-mode", num_options, options); 95 96 if ((media || output_bin || print_color_mode || print_quality || sides) && 97 !ppd->cache) 98 { 99 /* 100 * Load PPD cache and mapping data as needed... 101 */ 102 103 ppd->cache = _ppdCacheCreateWithPPD(ppd); 104 } 105 106 cache = ppd->cache; 107 108 if (media) 109 { 110 /* 111 * Loop through the option string, separating it at commas and marking each 112 * individual option as long as the corresponding PPD option (PageSize, 113 * InputSlot, etc.) is not also set. 114 * 115 * For PageSize, we also check for an empty option value since some versions 116 * of macOS use it to specify auto-selection of the media based solely on 117 * the size. 118 */ 119 120 for (val = media; *val;) 121 { 122 /* 123 * Extract the sub-option from the string... 124 */ 125 126 for (ptr = s; *val && *val != ',' && (size_t)(ptr - s) < (sizeof(s) - 1);) 127 *ptr++ = *val++; 128 *ptr++ = '\0'; 129 130 if (*val == ',') 131 val ++; 132 133 /* 134 * Mark it... 135 */ 136 137 if (!page_size || !page_size[0]) 138 { 139 if (!_cups_strncasecmp(s, "Custom.", 7) || ppdPageSize(ppd, s)) 140 ppd_mark_option(ppd, "PageSize", s); 141 else if ((ppd_keyword = _ppdCacheGetPageSize(cache, NULL, s, NULL)) != NULL) 142 ppd_mark_option(ppd, "PageSize", ppd_keyword); 143 } 144 145 if (cache && cache->source_option && 146 !cupsGetOption(cache->source_option, num_options, options) && 147 (ppd_keyword = _ppdCacheGetInputSlot(cache, NULL, s)) != NULL) 148 ppd_mark_option(ppd, cache->source_option, ppd_keyword); 149 150 if (!cupsGetOption("MediaType", num_options, options) && 151 (ppd_keyword = _ppdCacheGetMediaType(cache, NULL, s)) != NULL) 152 ppd_mark_option(ppd, "MediaType", ppd_keyword); 153 } 154 } 155 156 if (cache) 157 { 158 if (!cupsGetOption("com.apple.print.DocumentTicket.PMSpoolFormat", 159 num_options, options) && 160 !cupsGetOption("APPrinterPreset", num_options, options) && 161 (print_color_mode || print_quality)) 162 { 163 /* 164 * Map output-mode and print-quality to a preset... 165 */ 166 167 _pwg_print_color_mode_t pwg_pcm;/* print-color-mode index */ 168 _pwg_print_quality_t pwg_pq; /* print-quality index */ 169 cups_option_t *preset;/* Current preset option */ 170 171 if (print_color_mode && !strcmp(print_color_mode, "monochrome")) 172 pwg_pcm = _PWG_PRINT_COLOR_MODE_MONOCHROME; 173 else 174 pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR; 175 176 if (print_quality) 177 { 178 pwg_pq = (_pwg_print_quality_t)(atoi(print_quality) - IPP_QUALITY_DRAFT); 179 if (pwg_pq < _PWG_PRINT_QUALITY_DRAFT) 180 pwg_pq = _PWG_PRINT_QUALITY_DRAFT; 181 else if (pwg_pq > _PWG_PRINT_QUALITY_HIGH) 182 pwg_pq = _PWG_PRINT_QUALITY_HIGH; 183 } 184 else 185 pwg_pq = _PWG_PRINT_QUALITY_NORMAL; 186 187 if (cache->num_presets[pwg_pcm][pwg_pq] == 0) 188 { 189 /* 190 * Try to find a preset that works so that we maximize the chances of us 191 * getting a good print using IPP attributes. 192 */ 193 194 if (cache->num_presets[pwg_pcm][_PWG_PRINT_QUALITY_NORMAL] > 0) 195 pwg_pq = _PWG_PRINT_QUALITY_NORMAL; 196 else if (cache->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][pwg_pq] > 0) 197 pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR; 198 else 199 { 200 pwg_pq = _PWG_PRINT_QUALITY_NORMAL; 201 pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR; 202 } 203 } 204 205 if (cache->num_presets[pwg_pcm][pwg_pq] > 0) 206 { 207 /* 208 * Copy the preset options as long as the corresponding names are not 209 * already defined in the IPP request... 210 */ 211 212 for (i = cache->num_presets[pwg_pcm][pwg_pq], 213 preset = cache->presets[pwg_pcm][pwg_pq]; 214 i > 0; 215 i --, preset ++) 216 { 217 if (!cupsGetOption(preset->name, num_options, options)) 218 ppd_mark_option(ppd, preset->name, preset->value); 219 } 220 } 221 } 222 223 if (output_bin && !cupsGetOption("OutputBin", num_options, options) && 224 (ppd_keyword = _ppdCacheGetOutputBin(cache, output_bin)) != NULL) 225 { 226 /* 227 * Map output-bin to OutputBin... 228 */ 229 230 ppd_mark_option(ppd, "OutputBin", ppd_keyword); 231 } 232 233 if (sides && cache->sides_option && 234 !cupsGetOption(cache->sides_option, num_options, options)) 235 { 236 /* 237 * Map sides to duplex option... 238 */ 239 240 if (!strcmp(sides, "one-sided") && cache->sides_1sided) 241 ppd_mark_option(ppd, cache->sides_option, cache->sides_1sided); 242 else if (!strcmp(sides, "two-sided-long-edge") && 243 cache->sides_2sided_long) 244 ppd_mark_option(ppd, cache->sides_option, cache->sides_2sided_long); 245 else if (!strcmp(sides, "two-sided-short-edge") && 246 cache->sides_2sided_short) 247 ppd_mark_option(ppd, cache->sides_option, cache->sides_2sided_short); 248 } 249 } 250 251 /* 252 * Mark other options... 253 */ 254 255 for (i = num_options, optptr = options; i > 0; i --, optptr ++) 256 if (!_cups_strcasecmp(optptr->name, "media") || 257 !_cups_strcasecmp(optptr->name, "output-bin") || 258 !_cups_strcasecmp(optptr->name, "output-mode") || 259 !_cups_strcasecmp(optptr->name, "print-quality") || 260 !_cups_strcasecmp(optptr->name, "sides")) 261 continue; 262 else if (!_cups_strcasecmp(optptr->name, "resolution") || 263 !_cups_strcasecmp(optptr->name, "printer-resolution")) 264 { 265 ppd_mark_option(ppd, "Resolution", optptr->value); 266 ppd_mark_option(ppd, "SetResolution", optptr->value); 267 /* Calcomp, Linotype, QMS, Summagraphics, Tektronix, Varityper */ 268 ppd_mark_option(ppd, "JCLResolution", optptr->value); 269 /* HP */ 270 ppd_mark_option(ppd, "CNRes_PGP", optptr->value); 271 /* Canon */ 272 } 273 else if (!_cups_strcasecmp(optptr->name, "multiple-document-handling")) 274 { 275 if (!cupsGetOption("Collate", num_options, options) && 276 ppdFindOption(ppd, "Collate")) 277 { 278 if (_cups_strcasecmp(optptr->value, "separate-documents-uncollated-copies")) 279 ppd_mark_option(ppd, "Collate", "True"); 280 else 281 ppd_mark_option(ppd, "Collate", "False"); 282 } 283 } 284 else if (!_cups_strcasecmp(optptr->name, "finishings")) 285 { 286 /* 287 * Lookup cupsIPPFinishings attributes for each value... 288 */ 289 290 for (ptr = optptr->value; *ptr;) 291 { 292 /* 293 * Get the next finishings number... 294 */ 295 296 if (!isdigit(*ptr & 255)) 297 break; 298 299 if ((j = (int)strtol(ptr, &ptr, 10)) < 3) 300 break; 301 302 /* 303 * Skip separator as needed... 304 */ 305 306 if (*ptr == ',') 307 ptr ++; 308 309 /* 310 * Look it up in the PPD file... 311 */ 312 313 sprintf(s, "%d", j); 314 315 if ((attr = ppdFindAttr(ppd, "cupsIPPFinishings", s)) == NULL) 316 continue; 317 318 /* 319 * Apply "*Option Choice" settings from the attribute value... 320 */ 321 322 ppd_mark_choices(ppd, attr->value); 323 } 324 } 325 else if (!_cups_strcasecmp(optptr->name, "APPrinterPreset")) 326 { 327 /* 328 * Lookup APPrinterPreset value... 329 */ 330 331 if ((attr = ppdFindAttr(ppd, "APPrinterPreset", optptr->value)) != NULL) 332 { 333 /* 334 * Apply "*Option Choice" settings from the attribute value... 335 */ 336 337 ppd_mark_choices(ppd, attr->value); 338 } 339 } 340 else if (!_cups_strcasecmp(optptr->name, "mirror")) 341 ppd_mark_option(ppd, "MirrorPrint", optptr->value); 342 else 343 ppd_mark_option(ppd, optptr->name, optptr->value); 344 345 ppd_debug_marked(ppd, "After..."); 346 347 return (ppdConflicts(ppd) > 0); 348 } 349 350 351 /* 352 * 'ppdFindChoice()' - Return a pointer to an option choice. 353 */ 354 355 ppd_choice_t * /* O - Choice pointer or @code NULL@ */ 356 ppdFindChoice(ppd_option_t *o, /* I - Pointer to option */ 357 const char *choice) /* I - Name of choice */ 358 { 359 int i; /* Looping var */ 360 ppd_choice_t *c; /* Current choice */ 361 362 363 if (!o || !choice) 364 return (NULL); 365 366 if (choice[0] == '{' || !_cups_strncasecmp(choice, "Custom.", 7)) 367 choice = "Custom"; 368 369 for (i = o->num_choices, c = o->choices; i > 0; i --, c ++) 370 if (!_cups_strcasecmp(c->choice, choice)) 371 return (c); 372 373 return (NULL); 374 } 375 376 377 /* 378 * 'ppdFindMarkedChoice()' - Return the marked choice for the specified option. 379 */ 380 381 ppd_choice_t * /* O - Pointer to choice or @code NULL@ */ 382 ppdFindMarkedChoice(ppd_file_t *ppd, /* I - PPD file */ 383 const char *option) /* I - Keyword/option name */ 384 { 385 ppd_choice_t key, /* Search key for choice */ 386 *marked; /* Marked choice */ 387 388 389 DEBUG_printf(("2ppdFindMarkedChoice(ppd=%p, option=\"%s\")", ppd, option)); 390 391 if ((key.option = ppdFindOption(ppd, option)) == NULL) 392 { 393 DEBUG_puts("3ppdFindMarkedChoice: Option not found, returning NULL"); 394 return (NULL); 395 } 396 397 marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key); 398 399 DEBUG_printf(("3ppdFindMarkedChoice: Returning %p(%s)...", marked, 400 marked ? marked->choice : "NULL")); 401 402 return (marked); 403 } 404 405 406 /* 407 * 'ppdFindOption()' - Return a pointer to the specified option. 408 */ 409 410 ppd_option_t * /* O - Pointer to option or @code NULL@ */ 411 ppdFindOption(ppd_file_t *ppd, /* I - PPD file data */ 412 const char *option) /* I - Option/Keyword name */ 413 { 414 /* 415 * Range check input... 416 */ 417 418 if (!ppd || !option) 419 return (NULL); 420 421 if (ppd->options) 422 { 423 /* 424 * Search in the array... 425 */ 426 427 ppd_option_t key; /* Option search key */ 428 429 430 strlcpy(key.keyword, option, sizeof(key.keyword)); 431 432 return ((ppd_option_t *)cupsArrayFind(ppd->options, &key)); 433 } 434 else 435 { 436 /* 437 * Search in each group... 438 */ 439 440 int i, j; /* Looping vars */ 441 ppd_group_t *group; /* Current group */ 442 ppd_option_t *optptr; /* Current option */ 443 444 445 for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++) 446 for (j = group->num_options, optptr = group->options; 447 j > 0; 448 j --, optptr ++) 449 if (!_cups_strcasecmp(optptr->keyword, option)) 450 return (optptr); 451 452 return (NULL); 453 } 454 } 455 456 457 /* 458 * 'ppdIsMarked()' - Check to see if an option is marked. 459 */ 460 461 int /* O - Non-zero if option is marked */ 462 ppdIsMarked(ppd_file_t *ppd, /* I - PPD file data */ 463 const char *option, /* I - Option/Keyword name */ 464 const char *choice) /* I - Choice name */ 465 { 466 ppd_choice_t key, /* Search key */ 467 *c; /* Choice pointer */ 468 469 470 if (!ppd) 471 return (0); 472 473 if ((key.option = ppdFindOption(ppd, option)) == NULL) 474 return (0); 475 476 if ((c = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) == NULL) 477 return (0); 478 479 return (!strcmp(c->choice, choice)); 480 } 481 482 483 /* 484 * 'ppdMarkDefaults()' - Mark all default options in the PPD file. 485 */ 486 487 void 488 ppdMarkDefaults(ppd_file_t *ppd) /* I - PPD file record */ 489 { 490 int i; /* Looping variables */ 491 ppd_group_t *g; /* Current group */ 492 ppd_choice_t *c; /* Current choice */ 493 494 495 if (!ppd) 496 return; 497 498 /* 499 * Clean out the marked array... 500 */ 501 502 for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked); 503 c; 504 c = (ppd_choice_t *)cupsArrayNext(ppd->marked)) 505 { 506 cupsArrayRemove(ppd->marked, c); 507 c->marked = 0; 508 } 509 510 /* 511 * Then repopulate it with the defaults... 512 */ 513 514 for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++) 515 ppd_defaults(ppd, g); 516 517 /* 518 * Finally, tag any conflicts (API compatibility) once at the end. 519 */ 520 521 ppdConflicts(ppd); 522 } 523 524 525 /* 526 * 'ppdMarkOption()' - Mark an option in a PPD file and return the number of 527 * conflicts. 528 */ 529 530 int /* O - Number of conflicts */ 531 ppdMarkOption(ppd_file_t *ppd, /* I - PPD file record */ 532 const char *option, /* I - Keyword */ 533 const char *choice) /* I - Option name */ 534 { 535 DEBUG_printf(("ppdMarkOption(ppd=%p, option=\"%s\", choice=\"%s\")", 536 ppd, option, choice)); 537 538 /* 539 * Range check input... 540 */ 541 542 if (!ppd || !option || !choice) 543 return (0); 544 545 /* 546 * Mark the option... 547 */ 548 549 ppd_mark_option(ppd, option, choice); 550 551 /* 552 * Return the number of conflicts... 553 */ 554 555 return (ppdConflicts(ppd)); 556 } 557 558 559 /* 560 * 'ppdFirstOption()' - Return the first option in the PPD file. 561 * 562 * Options are returned from all groups in ascending alphanumeric order. 563 * 564 * @since CUPS 1.2/macOS 10.5@ 565 */ 566 567 ppd_option_t * /* O - First option or @code NULL@ */ 568 ppdFirstOption(ppd_file_t *ppd) /* I - PPD file */ 569 { 570 if (!ppd) 571 return (NULL); 572 else 573 return ((ppd_option_t *)cupsArrayFirst(ppd->options)); 574 } 575 576 577 /* 578 * 'ppdNextOption()' - Return the next option in the PPD file. 579 * 580 * Options are returned from all groups in ascending alphanumeric order. 581 * 582 * @since CUPS 1.2/macOS 10.5@ 583 */ 584 585 ppd_option_t * /* O - Next option or @code NULL@ */ 586 ppdNextOption(ppd_file_t *ppd) /* I - PPD file */ 587 { 588 if (!ppd) 589 return (NULL); 590 else 591 return ((ppd_option_t *)cupsArrayNext(ppd->options)); 592 } 593 594 595 /* 596 * '_ppdParseOptions()' - Parse options from a PPD file. 597 * 598 * This function looks for strings of the form: 599 * 600 * *option choice ... *optionN choiceN 601 * property value ... propertyN valueN 602 * 603 * It stops when it finds a string that doesn't match this format. 604 */ 605 606 int /* O - Number of options */ 607 _ppdParseOptions( 608 const char *s, /* I - String to parse */ 609 int num_options, /* I - Number of options */ 610 cups_option_t **options, /* IO - Options */ 611 _ppd_parse_t which) /* I - What to parse */ 612 { 613 char option[PPD_MAX_NAME * 2 + 1], /* Current option/property */ 614 choice[PPD_MAX_NAME], /* Current choice/value */ 615 *ptr; /* Pointer into option or choice */ 616 617 618 if (!s) 619 return (num_options); 620 621 /* 622 * Read all of the "*Option Choice" and "property value" pairs from the 623 * string, add them to an options array as we go... 624 */ 625 626 while (*s) 627 { 628 /* 629 * Skip leading whitespace... 630 */ 631 632 while (_cups_isspace(*s)) 633 s ++; 634 635 /* 636 * Get the option/property name... 637 */ 638 639 ptr = option; 640 while (*s && !_cups_isspace(*s) && ptr < (option + sizeof(option) - 1)) 641 *ptr++ = *s++; 642 643 if (ptr == s || !_cups_isspace(*s)) 644 break; 645 646 *ptr = '\0'; 647 648 /* 649 * Get the choice... 650 */ 651 652 while (_cups_isspace(*s)) 653 s ++; 654 655 if (!*s) 656 break; 657 658 ptr = choice; 659 while (*s && !_cups_isspace(*s) && ptr < (choice + sizeof(choice) - 1)) 660 *ptr++ = *s++; 661 662 if (*s && !_cups_isspace(*s)) 663 break; 664 665 *ptr = '\0'; 666 667 /* 668 * Add it to the options array... 669 */ 670 671 if (option[0] == '*' && which != _PPD_PARSE_PROPERTIES) 672 num_options = cupsAddOption(option + 1, choice, num_options, options); 673 else if (option[0] != '*' && which != _PPD_PARSE_OPTIONS) 674 num_options = cupsAddOption(option, choice, num_options, options); 675 } 676 677 return (num_options); 678 } 679 680 681 #ifdef DEBUG 682 /* 683 * 'ppd_debug_marked()' - Output the marked array to stdout... 684 */ 685 686 static void 687 ppd_debug_marked(ppd_file_t *ppd, /* I - PPD file data */ 688 const char *title) /* I - Title for list */ 689 { 690 ppd_choice_t *c; /* Current choice */ 691 692 693 DEBUG_printf(("2cupsMarkOptions: %s", title)); 694 695 for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked); 696 c; 697 c = (ppd_choice_t *)cupsArrayNext(ppd->marked)) 698 DEBUG_printf(("2cupsMarkOptions: %s=%s", c->option->keyword, c->choice)); 699 } 700 #endif /* DEBUG */ 701 702 703 /* 704 * 'ppd_defaults()' - Set the defaults for this group and all sub-groups. 705 */ 706 707 static void 708 ppd_defaults(ppd_file_t *ppd, /* I - PPD file */ 709 ppd_group_t *g) /* I - Group to default */ 710 { 711 int i; /* Looping var */ 712 ppd_option_t *o; /* Current option */ 713 ppd_group_t *sg; /* Current sub-group */ 714 715 716 for (i = g->num_options, o = g->options; i > 0; i --, o ++) 717 if (_cups_strcasecmp(o->keyword, "PageRegion") != 0) 718 ppd_mark_option(ppd, o->keyword, o->defchoice); 719 720 for (i = g->num_subgroups, sg = g->subgroups; i > 0; i --, sg ++) 721 ppd_defaults(ppd, sg); 722 } 723 724 725 /* 726 * 'ppd_mark_choices()' - Mark one or more option choices from a string. 727 */ 728 729 static void 730 ppd_mark_choices(ppd_file_t *ppd, /* I - PPD file */ 731 const char *s) /* I - "*Option Choice ..." string */ 732 { 733 int i, /* Looping var */ 734 num_options; /* Number of options */ 735 cups_option_t *options, /* Options */ 736 *option; /* Current option */ 737 738 739 if (!s) 740 return; 741 742 options = NULL; 743 num_options = _ppdParseOptions(s, 0, &options, 0); 744 745 for (i = num_options, option = options; i > 0; i --, option ++) 746 ppd_mark_option(ppd, option->name, option->value); 747 748 cupsFreeOptions(num_options, options); 749 } 750 751 752 /* 753 * 'ppd_mark_option()' - Quick mark an option without checking for conflicts. 754 */ 755 756 static void 757 ppd_mark_option(ppd_file_t *ppd, /* I - PPD file */ 758 const char *option, /* I - Option name */ 759 const char *choice) /* I - Choice name */ 760 { 761 int i, j; /* Looping vars */ 762 ppd_option_t *o; /* Option pointer */ 763 ppd_choice_t *c, /* Choice pointer */ 764 *oldc, /* Old choice pointer */ 765 key; /* Search key for choice */ 766 struct lconv *loc; /* Locale data */ 767 768 769 DEBUG_printf(("7ppd_mark_option(ppd=%p, option=\"%s\", choice=\"%s\")", 770 ppd, option, choice)); 771 772 /* 773 * AP_D_InputSlot is the "default input slot" on macOS, and setting 774 * it clears the regular InputSlot choices... 775 */ 776 777 if (!_cups_strcasecmp(option, "AP_D_InputSlot")) 778 { 779 cupsArraySave(ppd->options); 780 781 if ((o = ppdFindOption(ppd, "InputSlot")) != NULL) 782 { 783 key.option = o; 784 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL) 785 { 786 oldc->marked = 0; 787 cupsArrayRemove(ppd->marked, oldc); 788 } 789 } 790 791 cupsArrayRestore(ppd->options); 792 } 793 794 /* 795 * Check for custom options... 796 */ 797 798 cupsArraySave(ppd->options); 799 800 o = ppdFindOption(ppd, option); 801 802 cupsArrayRestore(ppd->options); 803 804 if (!o) 805 return; 806 807 loc = localeconv(); 808 809 if (!_cups_strncasecmp(choice, "Custom.", 7)) 810 { 811 /* 812 * Handle a custom option... 813 */ 814 815 if ((c = ppdFindChoice(o, "Custom")) == NULL) 816 return; 817 818 if (!_cups_strcasecmp(option, "PageSize")) 819 { 820 /* 821 * Handle custom page sizes... 822 */ 823 824 ppdPageSize(ppd, choice); 825 } 826 else 827 { 828 /* 829 * Handle other custom options... 830 */ 831 832 ppd_coption_t *coption; /* Custom option */ 833 ppd_cparam_t *cparam; /* Custom parameter */ 834 char *units; /* Custom points units */ 835 836 837 if ((coption = ppdFindCustomOption(ppd, option)) != NULL) 838 { 839 if ((cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params)) == NULL) 840 return; 841 842 switch (cparam->type) 843 { 844 case PPD_CUSTOM_CURVE : 845 case PPD_CUSTOM_INVCURVE : 846 case PPD_CUSTOM_REAL : 847 cparam->current.custom_real = (float)_cupsStrScand(choice + 7, 848 NULL, loc); 849 break; 850 851 case PPD_CUSTOM_POINTS : 852 cparam->current.custom_points = (float)_cupsStrScand(choice + 7, 853 &units, 854 loc); 855 856 if (units) 857 { 858 if (!_cups_strcasecmp(units, "cm")) 859 cparam->current.custom_points *= 72.0f / 2.54f; 860 else if (!_cups_strcasecmp(units, "mm")) 861 cparam->current.custom_points *= 72.0f / 25.4f; 862 else if (!_cups_strcasecmp(units, "m")) 863 cparam->current.custom_points *= 72.0f / 0.0254f; 864 else if (!_cups_strcasecmp(units, "in")) 865 cparam->current.custom_points *= 72.0f; 866 else if (!_cups_strcasecmp(units, "ft")) 867 cparam->current.custom_points *= 12.0f * 72.0f; 868 } 869 break; 870 871 case PPD_CUSTOM_INT : 872 cparam->current.custom_int = atoi(choice + 7); 873 break; 874 875 case PPD_CUSTOM_PASSCODE : 876 case PPD_CUSTOM_PASSWORD : 877 case PPD_CUSTOM_STRING : 878 if (cparam->current.custom_string) 879 _cupsStrFree(cparam->current.custom_string); 880 881 cparam->current.custom_string = _cupsStrAlloc(choice + 7); 882 break; 883 } 884 } 885 } 886 887 /* 888 * Make sure that we keep the option marked below... 889 */ 890 891 choice = "Custom"; 892 } 893 else if (choice[0] == '{') 894 { 895 /* 896 * Handle multi-value custom options... 897 */ 898 899 ppd_coption_t *coption; /* Custom option */ 900 ppd_cparam_t *cparam; /* Custom parameter */ 901 char *units; /* Custom points units */ 902 int num_vals; /* Number of values */ 903 cups_option_t *vals, /* Values */ 904 *val; /* Value */ 905 906 907 if ((c = ppdFindChoice(o, "Custom")) == NULL) 908 return; 909 910 if ((coption = ppdFindCustomOption(ppd, option)) != NULL) 911 { 912 num_vals = cupsParseOptions(choice, 0, &vals); 913 914 for (i = 0, val = vals; i < num_vals; i ++, val ++) 915 { 916 if ((cparam = ppdFindCustomParam(coption, val->name)) == NULL) 917 continue; 918 919 switch (cparam->type) 920 { 921 case PPD_CUSTOM_CURVE : 922 case PPD_CUSTOM_INVCURVE : 923 case PPD_CUSTOM_REAL : 924 cparam->current.custom_real = (float)_cupsStrScand(val->value, 925 NULL, loc); 926 break; 927 928 case PPD_CUSTOM_POINTS : 929 cparam->current.custom_points = (float)_cupsStrScand(val->value, 930 &units, 931 loc); 932 933 if (units) 934 { 935 if (!_cups_strcasecmp(units, "cm")) 936 cparam->current.custom_points *= 72.0f / 2.54f; 937 else if (!_cups_strcasecmp(units, "mm")) 938 cparam->current.custom_points *= 72.0f / 25.4f; 939 else if (!_cups_strcasecmp(units, "m")) 940 cparam->current.custom_points *= 72.0f / 0.0254f; 941 else if (!_cups_strcasecmp(units, "in")) 942 cparam->current.custom_points *= 72.0f; 943 else if (!_cups_strcasecmp(units, "ft")) 944 cparam->current.custom_points *= 12.0f * 72.0f; 945 } 946 break; 947 948 case PPD_CUSTOM_INT : 949 cparam->current.custom_int = atoi(val->value); 950 break; 951 952 case PPD_CUSTOM_PASSCODE : 953 case PPD_CUSTOM_PASSWORD : 954 case PPD_CUSTOM_STRING : 955 if (cparam->current.custom_string) 956 _cupsStrFree(cparam->current.custom_string); 957 958 cparam->current.custom_string = _cupsStrRetain(val->value); 959 break; 960 } 961 } 962 963 cupsFreeOptions(num_vals, vals); 964 } 965 } 966 else 967 { 968 for (i = o->num_choices, c = o->choices; i > 0; i --, c ++) 969 if (!_cups_strcasecmp(c->choice, choice)) 970 break; 971 972 if (!i) 973 return; 974 } 975 976 /* 977 * Option found; mark it and then handle unmarking any other options. 978 */ 979 980 if (o->ui != PPD_UI_PICKMANY) 981 { 982 /* 983 * Unmark all other choices... 984 */ 985 986 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, c)) != NULL) 987 { 988 oldc->marked = 0; 989 cupsArrayRemove(ppd->marked, oldc); 990 } 991 992 if (!_cups_strcasecmp(option, "PageSize") || !_cups_strcasecmp(option, "PageRegion")) 993 { 994 /* 995 * Mark current page size... 996 */ 997 998 for (j = 0; j < ppd->num_sizes; j ++) 999 ppd->sizes[j].marked = !_cups_strcasecmp(ppd->sizes[j].name, 1000 choice); 1001 1002 /* 1003 * Unmark the current PageSize or PageRegion setting, as 1004 * appropriate... 1005 */ 1006 1007 cupsArraySave(ppd->options); 1008 1009 if (!_cups_strcasecmp(option, "PageSize")) 1010 { 1011 if ((o = ppdFindOption(ppd, "PageRegion")) != NULL) 1012 { 1013 key.option = o; 1014 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL) 1015 { 1016 oldc->marked = 0; 1017 cupsArrayRemove(ppd->marked, oldc); 1018 } 1019 } 1020 } 1021 else 1022 { 1023 if ((o = ppdFindOption(ppd, "PageSize")) != NULL) 1024 { 1025 key.option = o; 1026 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL) 1027 { 1028 oldc->marked = 0; 1029 cupsArrayRemove(ppd->marked, oldc); 1030 } 1031 } 1032 } 1033 1034 cupsArrayRestore(ppd->options); 1035 } 1036 else if (!_cups_strcasecmp(option, "InputSlot")) 1037 { 1038 /* 1039 * Unmark ManualFeed option... 1040 */ 1041 1042 cupsArraySave(ppd->options); 1043 1044 if ((o = ppdFindOption(ppd, "ManualFeed")) != NULL) 1045 { 1046 key.option = o; 1047 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL) 1048 { 1049 oldc->marked = 0; 1050 cupsArrayRemove(ppd->marked, oldc); 1051 } 1052 } 1053 1054 cupsArrayRestore(ppd->options); 1055 } 1056 else if (!_cups_strcasecmp(option, "ManualFeed") && 1057 !_cups_strcasecmp(choice, "True")) 1058 { 1059 /* 1060 * Unmark InputSlot option... 1061 */ 1062 1063 cupsArraySave(ppd->options); 1064 1065 if ((o = ppdFindOption(ppd, "InputSlot")) != NULL) 1066 { 1067 key.option = o; 1068 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL) 1069 { 1070 oldc->marked = 0; 1071 cupsArrayRemove(ppd->marked, oldc); 1072 } 1073 } 1074 1075 cupsArrayRestore(ppd->options); 1076 } 1077 } 1078 1079 c->marked = 1; 1080 1081 cupsArrayAdd(ppd->marked, c); 1082 } 1083