1 /* 2 * Option marking routines for CUPS. 3 * 4 * Copyright 2007-2017 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 { 257 if (!_cups_strcasecmp(optptr->name, "media") || 258 !_cups_strcasecmp(optptr->name, "output-bin") || 259 !_cups_strcasecmp(optptr->name, "output-mode") || 260 !_cups_strcasecmp(optptr->name, "print-quality") || 261 !_cups_strcasecmp(optptr->name, "sides")) 262 continue; 263 else if (!_cups_strcasecmp(optptr->name, "resolution") || 264 !_cups_strcasecmp(optptr->name, "printer-resolution")) 265 { 266 ppd_mark_option(ppd, "Resolution", optptr->value); 267 ppd_mark_option(ppd, "SetResolution", optptr->value); 268 /* Calcomp, Linotype, QMS, Summagraphics, Tektronix, Varityper */ 269 ppd_mark_option(ppd, "JCLResolution", optptr->value); 270 /* HP */ 271 ppd_mark_option(ppd, "CNRes_PGP", optptr->value); 272 /* Canon */ 273 } 274 else if (!_cups_strcasecmp(optptr->name, "multiple-document-handling")) 275 { 276 if (!cupsGetOption("Collate", num_options, options) && 277 ppdFindOption(ppd, "Collate")) 278 { 279 if (_cups_strcasecmp(optptr->value, "separate-documents-uncollated-copies")) 280 ppd_mark_option(ppd, "Collate", "True"); 281 else 282 ppd_mark_option(ppd, "Collate", "False"); 283 } 284 } 285 else if (!_cups_strcasecmp(optptr->name, "finishings")) 286 { 287 /* 288 * Lookup cupsIPPFinishings attributes for each value... 289 */ 290 291 for (ptr = optptr->value; *ptr;) 292 { 293 /* 294 * Get the next finishings number... 295 */ 296 297 if (!isdigit(*ptr & 255)) 298 break; 299 300 if ((j = (int)strtol(ptr, &ptr, 10)) < 3) 301 break; 302 303 /* 304 * Skip separator as needed... 305 */ 306 307 if (*ptr == ',') 308 ptr ++; 309 310 /* 311 * Look it up in the PPD file... 312 */ 313 314 sprintf(s, "%d", j); 315 316 if ((attr = ppdFindAttr(ppd, "cupsIPPFinishings", s)) == NULL) 317 continue; 318 319 /* 320 * Apply "*Option Choice" settings from the attribute value... 321 */ 322 323 ppd_mark_choices(ppd, attr->value); 324 } 325 } 326 else if (!_cups_strcasecmp(optptr->name, "APPrinterPreset")) 327 { 328 /* 329 * Lookup APPrinterPreset value... 330 */ 331 332 if ((attr = ppdFindAttr(ppd, "APPrinterPreset", optptr->value)) != NULL) 333 { 334 /* 335 * Apply "*Option Choice" settings from the attribute value... 336 */ 337 338 ppd_mark_choices(ppd, attr->value); 339 } 340 } 341 else if (!_cups_strcasecmp(optptr->name, "mirror")) 342 ppd_mark_option(ppd, "MirrorPrint", optptr->value); 343 else 344 ppd_mark_option(ppd, optptr->name, optptr->value); 345 } 346 347 if (print_quality) 348 { 349 int pq = atoi(print_quality); /* print-quaity value */ 350 351 if (pq == IPP_QUALITY_DRAFT) 352 ppd_mark_option(ppd, "cupsPrintQuality", "Draft"); 353 else if (pq == IPP_QUALITY_HIGH) 354 ppd_mark_option(ppd, "cupsPrintQuality", "High"); 355 else 356 ppd_mark_option(ppd, "cupsPrintQuality", "Normal"); 357 } 358 359 ppd_debug_marked(ppd, "After..."); 360 361 return (ppdConflicts(ppd) > 0); 362 } 363 364 365 /* 366 * 'ppdFindChoice()' - Return a pointer to an option choice. 367 */ 368 369 ppd_choice_t * /* O - Choice pointer or @code NULL@ */ 370 ppdFindChoice(ppd_option_t *o, /* I - Pointer to option */ 371 const char *choice) /* I - Name of choice */ 372 { 373 int i; /* Looping var */ 374 ppd_choice_t *c; /* Current choice */ 375 376 377 if (!o || !choice) 378 return (NULL); 379 380 if (choice[0] == '{' || !_cups_strncasecmp(choice, "Custom.", 7)) 381 choice = "Custom"; 382 383 for (i = o->num_choices, c = o->choices; i > 0; i --, c ++) 384 if (!_cups_strcasecmp(c->choice, choice)) 385 return (c); 386 387 return (NULL); 388 } 389 390 391 /* 392 * 'ppdFindMarkedChoice()' - Return the marked choice for the specified option. 393 */ 394 395 ppd_choice_t * /* O - Pointer to choice or @code NULL@ */ 396 ppdFindMarkedChoice(ppd_file_t *ppd, /* I - PPD file */ 397 const char *option) /* I - Keyword/option name */ 398 { 399 ppd_choice_t key, /* Search key for choice */ 400 *marked; /* Marked choice */ 401 402 403 DEBUG_printf(("2ppdFindMarkedChoice(ppd=%p, option=\"%s\")", ppd, option)); 404 405 if ((key.option = ppdFindOption(ppd, option)) == NULL) 406 { 407 DEBUG_puts("3ppdFindMarkedChoice: Option not found, returning NULL"); 408 return (NULL); 409 } 410 411 marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key); 412 413 DEBUG_printf(("3ppdFindMarkedChoice: Returning %p(%s)...", marked, 414 marked ? marked->choice : "NULL")); 415 416 return (marked); 417 } 418 419 420 /* 421 * 'ppdFindOption()' - Return a pointer to the specified option. 422 */ 423 424 ppd_option_t * /* O - Pointer to option or @code NULL@ */ 425 ppdFindOption(ppd_file_t *ppd, /* I - PPD file data */ 426 const char *option) /* I - Option/Keyword name */ 427 { 428 /* 429 * Range check input... 430 */ 431 432 if (!ppd || !option) 433 return (NULL); 434 435 if (ppd->options) 436 { 437 /* 438 * Search in the array... 439 */ 440 441 ppd_option_t key; /* Option search key */ 442 443 444 strlcpy(key.keyword, option, sizeof(key.keyword)); 445 446 return ((ppd_option_t *)cupsArrayFind(ppd->options, &key)); 447 } 448 else 449 { 450 /* 451 * Search in each group... 452 */ 453 454 int i, j; /* Looping vars */ 455 ppd_group_t *group; /* Current group */ 456 ppd_option_t *optptr; /* Current option */ 457 458 459 for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++) 460 for (j = group->num_options, optptr = group->options; 461 j > 0; 462 j --, optptr ++) 463 if (!_cups_strcasecmp(optptr->keyword, option)) 464 return (optptr); 465 466 return (NULL); 467 } 468 } 469 470 471 /* 472 * 'ppdIsMarked()' - Check to see if an option is marked. 473 */ 474 475 int /* O - Non-zero if option is marked */ 476 ppdIsMarked(ppd_file_t *ppd, /* I - PPD file data */ 477 const char *option, /* I - Option/Keyword name */ 478 const char *choice) /* I - Choice name */ 479 { 480 ppd_choice_t key, /* Search key */ 481 *c; /* Choice pointer */ 482 483 484 if (!ppd) 485 return (0); 486 487 if ((key.option = ppdFindOption(ppd, option)) == NULL) 488 return (0); 489 490 if ((c = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) == NULL) 491 return (0); 492 493 return (!strcmp(c->choice, choice)); 494 } 495 496 497 /* 498 * 'ppdMarkDefaults()' - Mark all default options in the PPD file. 499 */ 500 501 void 502 ppdMarkDefaults(ppd_file_t *ppd) /* I - PPD file record */ 503 { 504 int i; /* Looping variables */ 505 ppd_group_t *g; /* Current group */ 506 ppd_choice_t *c; /* Current choice */ 507 508 509 if (!ppd) 510 return; 511 512 /* 513 * Clean out the marked array... 514 */ 515 516 for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked); 517 c; 518 c = (ppd_choice_t *)cupsArrayNext(ppd->marked)) 519 { 520 cupsArrayRemove(ppd->marked, c); 521 c->marked = 0; 522 } 523 524 /* 525 * Then repopulate it with the defaults... 526 */ 527 528 for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++) 529 ppd_defaults(ppd, g); 530 531 /* 532 * Finally, tag any conflicts (API compatibility) once at the end. 533 */ 534 535 ppdConflicts(ppd); 536 } 537 538 539 /* 540 * 'ppdMarkOption()' - Mark an option in a PPD file and return the number of 541 * conflicts. 542 */ 543 544 int /* O - Number of conflicts */ 545 ppdMarkOption(ppd_file_t *ppd, /* I - PPD file record */ 546 const char *option, /* I - Keyword */ 547 const char *choice) /* I - Option name */ 548 { 549 DEBUG_printf(("ppdMarkOption(ppd=%p, option=\"%s\", choice=\"%s\")", 550 ppd, option, choice)); 551 552 /* 553 * Range check input... 554 */ 555 556 if (!ppd || !option || !choice) 557 return (0); 558 559 /* 560 * Mark the option... 561 */ 562 563 ppd_mark_option(ppd, option, choice); 564 565 /* 566 * Return the number of conflicts... 567 */ 568 569 return (ppdConflicts(ppd)); 570 } 571 572 573 /* 574 * 'ppdFirstOption()' - Return the first option in the PPD file. 575 * 576 * Options are returned from all groups in ascending alphanumeric order. 577 * 578 * @since CUPS 1.2/macOS 10.5@ 579 */ 580 581 ppd_option_t * /* O - First option or @code NULL@ */ 582 ppdFirstOption(ppd_file_t *ppd) /* I - PPD file */ 583 { 584 if (!ppd) 585 return (NULL); 586 else 587 return ((ppd_option_t *)cupsArrayFirst(ppd->options)); 588 } 589 590 591 /* 592 * 'ppdNextOption()' - Return the next option in the PPD file. 593 * 594 * Options are returned from all groups in ascending alphanumeric order. 595 * 596 * @since CUPS 1.2/macOS 10.5@ 597 */ 598 599 ppd_option_t * /* O - Next option or @code NULL@ */ 600 ppdNextOption(ppd_file_t *ppd) /* I - PPD file */ 601 { 602 if (!ppd) 603 return (NULL); 604 else 605 return ((ppd_option_t *)cupsArrayNext(ppd->options)); 606 } 607 608 609 /* 610 * '_ppdParseOptions()' - Parse options from a PPD file. 611 * 612 * This function looks for strings of the form: 613 * 614 * *option choice ... *optionN choiceN 615 * property value ... propertyN valueN 616 * 617 * It stops when it finds a string that doesn't match this format. 618 */ 619 620 int /* O - Number of options */ 621 _ppdParseOptions( 622 const char *s, /* I - String to parse */ 623 int num_options, /* I - Number of options */ 624 cups_option_t **options, /* IO - Options */ 625 _ppd_parse_t which) /* I - What to parse */ 626 { 627 char option[PPD_MAX_NAME * 2 + 1], /* Current option/property */ 628 choice[PPD_MAX_NAME], /* Current choice/value */ 629 *ptr; /* Pointer into option or choice */ 630 631 632 if (!s) 633 return (num_options); 634 635 /* 636 * Read all of the "*Option Choice" and "property value" pairs from the 637 * string, add them to an options array as we go... 638 */ 639 640 while (*s) 641 { 642 /* 643 * Skip leading whitespace... 644 */ 645 646 while (_cups_isspace(*s)) 647 s ++; 648 649 /* 650 * Get the option/property name... 651 */ 652 653 ptr = option; 654 while (*s && !_cups_isspace(*s) && ptr < (option + sizeof(option) - 1)) 655 *ptr++ = *s++; 656 657 if (ptr == s || !_cups_isspace(*s)) 658 break; 659 660 *ptr = '\0'; 661 662 /* 663 * Get the choice... 664 */ 665 666 while (_cups_isspace(*s)) 667 s ++; 668 669 if (!*s) 670 break; 671 672 ptr = choice; 673 while (*s && !_cups_isspace(*s) && ptr < (choice + sizeof(choice) - 1)) 674 *ptr++ = *s++; 675 676 if (*s && !_cups_isspace(*s)) 677 break; 678 679 *ptr = '\0'; 680 681 /* 682 * Add it to the options array... 683 */ 684 685 if (option[0] == '*' && which != _PPD_PARSE_PROPERTIES) 686 num_options = cupsAddOption(option + 1, choice, num_options, options); 687 else if (option[0] != '*' && which != _PPD_PARSE_OPTIONS) 688 num_options = cupsAddOption(option, choice, num_options, options); 689 } 690 691 return (num_options); 692 } 693 694 695 #ifdef DEBUG 696 /* 697 * 'ppd_debug_marked()' - Output the marked array to stdout... 698 */ 699 700 static void 701 ppd_debug_marked(ppd_file_t *ppd, /* I - PPD file data */ 702 const char *title) /* I - Title for list */ 703 { 704 ppd_choice_t *c; /* Current choice */ 705 706 707 DEBUG_printf(("2cupsMarkOptions: %s", title)); 708 709 for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked); 710 c; 711 c = (ppd_choice_t *)cupsArrayNext(ppd->marked)) 712 DEBUG_printf(("2cupsMarkOptions: %s=%s", c->option->keyword, c->choice)); 713 } 714 #endif /* DEBUG */ 715 716 717 /* 718 * 'ppd_defaults()' - Set the defaults for this group and all sub-groups. 719 */ 720 721 static void 722 ppd_defaults(ppd_file_t *ppd, /* I - PPD file */ 723 ppd_group_t *g) /* I - Group to default */ 724 { 725 int i; /* Looping var */ 726 ppd_option_t *o; /* Current option */ 727 ppd_group_t *sg; /* Current sub-group */ 728 729 730 for (i = g->num_options, o = g->options; i > 0; i --, o ++) 731 if (_cups_strcasecmp(o->keyword, "PageRegion") != 0) 732 ppd_mark_option(ppd, o->keyword, o->defchoice); 733 734 for (i = g->num_subgroups, sg = g->subgroups; i > 0; i --, sg ++) 735 ppd_defaults(ppd, sg); 736 } 737 738 739 /* 740 * 'ppd_mark_choices()' - Mark one or more option choices from a string. 741 */ 742 743 static void 744 ppd_mark_choices(ppd_file_t *ppd, /* I - PPD file */ 745 const char *s) /* I - "*Option Choice ..." string */ 746 { 747 int i, /* Looping var */ 748 num_options; /* Number of options */ 749 cups_option_t *options, /* Options */ 750 *option; /* Current option */ 751 752 753 if (!s) 754 return; 755 756 options = NULL; 757 num_options = _ppdParseOptions(s, 0, &options, 0); 758 759 for (i = num_options, option = options; i > 0; i --, option ++) 760 ppd_mark_option(ppd, option->name, option->value); 761 762 cupsFreeOptions(num_options, options); 763 } 764 765 766 /* 767 * 'ppd_mark_option()' - Quick mark an option without checking for conflicts. 768 */ 769 770 static void 771 ppd_mark_option(ppd_file_t *ppd, /* I - PPD file */ 772 const char *option, /* I - Option name */ 773 const char *choice) /* I - Choice name */ 774 { 775 int i, j; /* Looping vars */ 776 ppd_option_t *o; /* Option pointer */ 777 ppd_choice_t *c, /* Choice pointer */ 778 *oldc, /* Old choice pointer */ 779 key; /* Search key for choice */ 780 struct lconv *loc; /* Locale data */ 781 782 783 DEBUG_printf(("7ppd_mark_option(ppd=%p, option=\"%s\", choice=\"%s\")", 784 ppd, option, choice)); 785 786 /* 787 * AP_D_InputSlot is the "default input slot" on macOS, and setting 788 * it clears the regular InputSlot choices... 789 */ 790 791 if (!_cups_strcasecmp(option, "AP_D_InputSlot")) 792 { 793 cupsArraySave(ppd->options); 794 795 if ((o = ppdFindOption(ppd, "InputSlot")) != NULL) 796 { 797 key.option = o; 798 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL) 799 { 800 oldc->marked = 0; 801 cupsArrayRemove(ppd->marked, oldc); 802 } 803 } 804 805 cupsArrayRestore(ppd->options); 806 } 807 808 /* 809 * Check for custom options... 810 */ 811 812 cupsArraySave(ppd->options); 813 814 o = ppdFindOption(ppd, option); 815 816 cupsArrayRestore(ppd->options); 817 818 if (!o) 819 return; 820 821 loc = localeconv(); 822 823 if (!_cups_strncasecmp(choice, "Custom.", 7)) 824 { 825 /* 826 * Handle a custom option... 827 */ 828 829 if ((c = ppdFindChoice(o, "Custom")) == NULL) 830 return; 831 832 if (!_cups_strcasecmp(option, "PageSize")) 833 { 834 /* 835 * Handle custom page sizes... 836 */ 837 838 ppdPageSize(ppd, choice); 839 } 840 else 841 { 842 /* 843 * Handle other custom options... 844 */ 845 846 ppd_coption_t *coption; /* Custom option */ 847 ppd_cparam_t *cparam; /* Custom parameter */ 848 char *units; /* Custom points units */ 849 850 851 if ((coption = ppdFindCustomOption(ppd, option)) != NULL) 852 { 853 if ((cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params)) == NULL) 854 return; 855 856 switch (cparam->type) 857 { 858 case PPD_CUSTOM_CURVE : 859 case PPD_CUSTOM_INVCURVE : 860 case PPD_CUSTOM_REAL : 861 cparam->current.custom_real = (float)_cupsStrScand(choice + 7, 862 NULL, loc); 863 break; 864 865 case PPD_CUSTOM_POINTS : 866 cparam->current.custom_points = (float)_cupsStrScand(choice + 7, 867 &units, 868 loc); 869 870 if (units) 871 { 872 if (!_cups_strcasecmp(units, "cm")) 873 cparam->current.custom_points *= 72.0f / 2.54f; 874 else if (!_cups_strcasecmp(units, "mm")) 875 cparam->current.custom_points *= 72.0f / 25.4f; 876 else if (!_cups_strcasecmp(units, "m")) 877 cparam->current.custom_points *= 72.0f / 0.0254f; 878 else if (!_cups_strcasecmp(units, "in")) 879 cparam->current.custom_points *= 72.0f; 880 else if (!_cups_strcasecmp(units, "ft")) 881 cparam->current.custom_points *= 12.0f * 72.0f; 882 } 883 break; 884 885 case PPD_CUSTOM_INT : 886 cparam->current.custom_int = atoi(choice + 7); 887 break; 888 889 case PPD_CUSTOM_PASSCODE : 890 case PPD_CUSTOM_PASSWORD : 891 case PPD_CUSTOM_STRING : 892 if (cparam->current.custom_string) 893 _cupsStrFree(cparam->current.custom_string); 894 895 cparam->current.custom_string = _cupsStrAlloc(choice + 7); 896 break; 897 } 898 } 899 } 900 901 /* 902 * Make sure that we keep the option marked below... 903 */ 904 905 choice = "Custom"; 906 } 907 else if (choice[0] == '{') 908 { 909 /* 910 * Handle multi-value custom options... 911 */ 912 913 ppd_coption_t *coption; /* Custom option */ 914 ppd_cparam_t *cparam; /* Custom parameter */ 915 char *units; /* Custom points units */ 916 int num_vals; /* Number of values */ 917 cups_option_t *vals, /* Values */ 918 *val; /* Value */ 919 920 921 if ((c = ppdFindChoice(o, "Custom")) == NULL) 922 return; 923 924 if ((coption = ppdFindCustomOption(ppd, option)) != NULL) 925 { 926 num_vals = cupsParseOptions(choice, 0, &vals); 927 928 for (i = 0, val = vals; i < num_vals; i ++, val ++) 929 { 930 if ((cparam = ppdFindCustomParam(coption, val->name)) == NULL) 931 continue; 932 933 switch (cparam->type) 934 { 935 case PPD_CUSTOM_CURVE : 936 case PPD_CUSTOM_INVCURVE : 937 case PPD_CUSTOM_REAL : 938 cparam->current.custom_real = (float)_cupsStrScand(val->value, 939 NULL, loc); 940 break; 941 942 case PPD_CUSTOM_POINTS : 943 cparam->current.custom_points = (float)_cupsStrScand(val->value, 944 &units, 945 loc); 946 947 if (units) 948 { 949 if (!_cups_strcasecmp(units, "cm")) 950 cparam->current.custom_points *= 72.0f / 2.54f; 951 else if (!_cups_strcasecmp(units, "mm")) 952 cparam->current.custom_points *= 72.0f / 25.4f; 953 else if (!_cups_strcasecmp(units, "m")) 954 cparam->current.custom_points *= 72.0f / 0.0254f; 955 else if (!_cups_strcasecmp(units, "in")) 956 cparam->current.custom_points *= 72.0f; 957 else if (!_cups_strcasecmp(units, "ft")) 958 cparam->current.custom_points *= 12.0f * 72.0f; 959 } 960 break; 961 962 case PPD_CUSTOM_INT : 963 cparam->current.custom_int = atoi(val->value); 964 break; 965 966 case PPD_CUSTOM_PASSCODE : 967 case PPD_CUSTOM_PASSWORD : 968 case PPD_CUSTOM_STRING : 969 if (cparam->current.custom_string) 970 _cupsStrFree(cparam->current.custom_string); 971 972 cparam->current.custom_string = _cupsStrRetain(val->value); 973 break; 974 } 975 } 976 977 cupsFreeOptions(num_vals, vals); 978 } 979 } 980 else 981 { 982 for (i = o->num_choices, c = o->choices; i > 0; i --, c ++) 983 if (!_cups_strcasecmp(c->choice, choice)) 984 break; 985 986 if (!i) 987 return; 988 } 989 990 /* 991 * Option found; mark it and then handle unmarking any other options. 992 */ 993 994 if (o->ui != PPD_UI_PICKMANY) 995 { 996 /* 997 * Unmark all other choices... 998 */ 999 1000 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, c)) != NULL) 1001 { 1002 oldc->marked = 0; 1003 cupsArrayRemove(ppd->marked, oldc); 1004 } 1005 1006 if (!_cups_strcasecmp(option, "PageSize") || !_cups_strcasecmp(option, "PageRegion")) 1007 { 1008 /* 1009 * Mark current page size... 1010 */ 1011 1012 for (j = 0; j < ppd->num_sizes; j ++) 1013 ppd->sizes[j].marked = !_cups_strcasecmp(ppd->sizes[j].name, 1014 choice); 1015 1016 /* 1017 * Unmark the current PageSize or PageRegion setting, as 1018 * appropriate... 1019 */ 1020 1021 cupsArraySave(ppd->options); 1022 1023 if (!_cups_strcasecmp(option, "PageSize")) 1024 { 1025 if ((o = ppdFindOption(ppd, "PageRegion")) != NULL) 1026 { 1027 key.option = o; 1028 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL) 1029 { 1030 oldc->marked = 0; 1031 cupsArrayRemove(ppd->marked, oldc); 1032 } 1033 } 1034 } 1035 else 1036 { 1037 if ((o = ppdFindOption(ppd, "PageSize")) != NULL) 1038 { 1039 key.option = o; 1040 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL) 1041 { 1042 oldc->marked = 0; 1043 cupsArrayRemove(ppd->marked, oldc); 1044 } 1045 } 1046 } 1047 1048 cupsArrayRestore(ppd->options); 1049 } 1050 else if (!_cups_strcasecmp(option, "InputSlot")) 1051 { 1052 /* 1053 * Unmark ManualFeed option... 1054 */ 1055 1056 cupsArraySave(ppd->options); 1057 1058 if ((o = ppdFindOption(ppd, "ManualFeed")) != NULL) 1059 { 1060 key.option = o; 1061 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL) 1062 { 1063 oldc->marked = 0; 1064 cupsArrayRemove(ppd->marked, oldc); 1065 } 1066 } 1067 1068 cupsArrayRestore(ppd->options); 1069 } 1070 else if (!_cups_strcasecmp(option, "ManualFeed") && 1071 !_cups_strcasecmp(choice, "True")) 1072 { 1073 /* 1074 * Unmark InputSlot option... 1075 */ 1076 1077 cupsArraySave(ppd->options); 1078 1079 if ((o = ppdFindOption(ppd, "InputSlot")) != NULL) 1080 { 1081 key.option = o; 1082 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL) 1083 { 1084 oldc->marked = 0; 1085 cupsArrayRemove(ppd->marked, oldc); 1086 } 1087 } 1088 1089 cupsArrayRestore(ppd->options); 1090 } 1091 } 1092 1093 c->marked = 1; 1094 1095 cupsArrayAdd(ppd->marked, c); 1096 } 1097