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