1 /* 2 * Option conflict management 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 constants... 28 */ 29 30 enum 31 { 32 _PPD_NORMAL_CONSTRAINTS, 33 _PPD_OPTION_CONSTRAINTS, 34 _PPD_INSTALLABLE_CONSTRAINTS, 35 _PPD_ALL_CONSTRAINTS 36 }; 37 38 39 /* 40 * Local functions... 41 */ 42 43 static int ppd_is_installable(ppd_group_t *installable, 44 const char *option); 45 static void ppd_load_constraints(ppd_file_t *ppd); 46 static cups_array_t *ppd_test_constraints(ppd_file_t *ppd, 47 const char *option, 48 const char *choice, 49 int num_options, 50 cups_option_t *options, 51 int which); 52 53 54 /* 55 * 'cupsGetConflicts()' - Get a list of conflicting options in a marked PPD. 56 * 57 * This function gets a list of options that would conflict if "option" and 58 * "choice" were marked in the PPD. You would typically call this function 59 * after marking the currently selected options in the PPD in order to 60 * determine whether a new option selection would cause a conflict. 61 * 62 * The number of conflicting options are returned with "options" pointing to 63 * the conflicting options. The returned option array must be freed using 64 * @link cupsFreeOptions@. 65 * 66 * @since CUPS 1.4/macOS 10.6@ 67 */ 68 69 int /* O - Number of conflicting options */ 70 cupsGetConflicts( 71 ppd_file_t *ppd, /* I - PPD file */ 72 const char *option, /* I - Option to test */ 73 const char *choice, /* I - Choice to test */ 74 cups_option_t **options) /* O - Conflicting options */ 75 { 76 int i, /* Looping var */ 77 num_options; /* Number of conflicting options */ 78 cups_array_t *active; /* Active conflicts */ 79 _ppd_cups_uiconsts_t *c; /* Current constraints */ 80 _ppd_cups_uiconst_t *cptr; /* Current constraint */ 81 ppd_choice_t *marked; /* Marked choice */ 82 83 84 /* 85 * Range check input... 86 */ 87 88 if (options) 89 *options = NULL; 90 91 if (!ppd || !option || !choice || !options) 92 return (0); 93 94 /* 95 * Test for conflicts... 96 */ 97 98 active = ppd_test_constraints(ppd, option, choice, 0, NULL, 99 _PPD_ALL_CONSTRAINTS); 100 101 /* 102 * Loop through all of the UI constraints and add any options that conflict... 103 */ 104 105 for (num_options = 0, c = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active); 106 c; 107 c = (_ppd_cups_uiconsts_t *)cupsArrayNext(active)) 108 { 109 for (i = c->num_constraints, cptr = c->constraints; 110 i > 0; 111 i --, cptr ++) 112 if (_cups_strcasecmp(cptr->option->keyword, option)) 113 { 114 if (cptr->choice) 115 num_options = cupsAddOption(cptr->option->keyword, 116 cptr->choice->choice, num_options, 117 options); 118 else if ((marked = ppdFindMarkedChoice(ppd, 119 cptr->option->keyword)) != NULL) 120 num_options = cupsAddOption(cptr->option->keyword, marked->choice, 121 num_options, options); 122 } 123 } 124 125 cupsArrayDelete(active); 126 127 return (num_options); 128 } 129 130 131 /* 132 * 'cupsResolveConflicts()' - Resolve conflicts in a marked PPD. 133 * 134 * This function attempts to resolve any conflicts in a marked PPD, returning 135 * a list of option changes that are required to resolve them. On input, 136 * "num_options" and "options" contain any pending option changes that have 137 * not yet been marked, while "option" and "choice" contain the most recent 138 * selection which may or may not be in "num_options" or "options". 139 * 140 * On successful return, "num_options" and "options" are updated to contain 141 * "option" and "choice" along with any changes required to resolve conflicts 142 * specified in the PPD file and 1 is returned. 143 * 144 * If option conflicts cannot be resolved, "num_options" and "options" are not 145 * changed and 0 is returned. 146 * 147 * When resolving conflicts, @code cupsResolveConflicts@ does not consider 148 * changes to the current page size (@code media@, @code PageSize@, and 149 * @code PageRegion@) or to the most recent option specified in "option". 150 * Thus, if the only way to resolve a conflict is to change the page size 151 * or the option the user most recently changed, @code cupsResolveConflicts@ 152 * will return 0 to indicate it was unable to resolve the conflicts. 153 * 154 * The @code cupsResolveConflicts@ function uses one of two sources of option 155 * constraint information. The preferred constraint information is defined by 156 * @code cupsUIConstraints@ and @code cupsUIResolver@ attributes - in this 157 * case, the PPD file provides constraint resolution actions. 158 * 159 * The backup constraint information is defined by the 160 * @code UIConstraints@ and @code NonUIConstraints@ attributes. These 161 * constraints are resolved algorithmically by first selecting the default 162 * choice for the conflicting option, then iterating over all possible choices 163 * until a non-conflicting option choice is found. 164 * 165 * @since CUPS 1.4/macOS 10.6@ 166 */ 167 168 int /* O - 1 on success, 0 on failure */ 169 cupsResolveConflicts( 170 ppd_file_t *ppd, /* I - PPD file */ 171 const char *option, /* I - Newly selected option or @code NULL@ for none */ 172 const char *choice, /* I - Newly selected choice or @code NULL@ for none */ 173 int *num_options, /* IO - Number of additional selected options */ 174 cups_option_t **options) /* IO - Additional selected options */ 175 { 176 int i, /* Looping var */ 177 tries, /* Number of tries */ 178 num_newopts; /* Number of new options */ 179 cups_option_t *newopts; /* New options */ 180 cups_array_t *active = NULL, /* Active constraints */ 181 *pass, /* Resolvers for this pass */ 182 *resolvers, /* Resolvers we have used */ 183 *test; /* Test array for conflicts */ 184 _ppd_cups_uiconsts_t *consts; /* Current constraints */ 185 _ppd_cups_uiconst_t *constptr; /* Current constraint */ 186 ppd_attr_t *resolver; /* Current resolver */ 187 const char *resval; /* Pointer into resolver value */ 188 char resoption[PPD_MAX_NAME], 189 /* Current resolver option */ 190 reschoice[PPD_MAX_NAME], 191 /* Current resolver choice */ 192 *resptr, /* Pointer into option/choice */ 193 firstpage[255]; /* AP_FIRSTPAGE_Keyword string */ 194 const char *value; /* Selected option value */ 195 int changed; /* Did we change anything? */ 196 ppd_choice_t *marked; /* Marked choice */ 197 198 199 /* 200 * Range check input... 201 */ 202 203 if (!ppd || !num_options || !options || (option == NULL) != (choice == NULL)) 204 return (0); 205 206 /* 207 * Build a shadow option array... 208 */ 209 210 num_newopts = 0; 211 newopts = NULL; 212 213 for (i = 0; i < *num_options; i ++) 214 num_newopts = cupsAddOption((*options)[i].name, (*options)[i].value, 215 num_newopts, &newopts); 216 if (option && _cups_strcasecmp(option, "Collate")) 217 num_newopts = cupsAddOption(option, choice, num_newopts, &newopts); 218 219 /* 220 * Loop until we have no conflicts... 221 */ 222 223 cupsArraySave(ppd->sorted_attrs); 224 225 resolvers = NULL; 226 pass = cupsArrayNew((cups_array_func_t)_cups_strcasecmp, NULL); 227 tries = 0; 228 229 while (tries < 100 && 230 (active = ppd_test_constraints(ppd, NULL, NULL, num_newopts, newopts, 231 _PPD_ALL_CONSTRAINTS)) != NULL) 232 { 233 tries ++; 234 235 if (!resolvers) 236 resolvers = cupsArrayNew((cups_array_func_t)_cups_strcasecmp, NULL); 237 238 for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active), changed = 0; 239 consts; 240 consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(active)) 241 { 242 if (consts->resolver[0]) 243 { 244 /* 245 * Look up the resolver... 246 */ 247 248 if (cupsArrayFind(pass, consts->resolver)) 249 continue; /* Already applied this resolver... */ 250 251 if (cupsArrayFind(resolvers, consts->resolver)) 252 { 253 /* 254 * Resolver loop! 255 */ 256 257 DEBUG_printf(("1cupsResolveConflicts: Resolver loop with %s!", 258 consts->resolver)); 259 goto error; 260 } 261 262 if ((resolver = ppdFindAttr(ppd, "cupsUIResolver", 263 consts->resolver)) == NULL) 264 { 265 DEBUG_printf(("1cupsResolveConflicts: Resolver %s not found!", 266 consts->resolver)); 267 goto error; 268 } 269 270 if (!resolver->value) 271 { 272 DEBUG_printf(("1cupsResolveConflicts: Resolver %s has no value!", 273 consts->resolver)); 274 goto error; 275 } 276 277 /* 278 * Add the options from the resolver... 279 */ 280 281 cupsArrayAdd(pass, consts->resolver); 282 cupsArrayAdd(resolvers, consts->resolver); 283 284 for (resval = resolver->value; *resval && !changed;) 285 { 286 while (_cups_isspace(*resval)) 287 resval ++; 288 289 if (*resval != '*') 290 break; 291 292 for (resval ++, resptr = resoption; 293 *resval && !_cups_isspace(*resval); 294 resval ++) 295 if (resptr < (resoption + sizeof(resoption) - 1)) 296 *resptr++ = *resval; 297 298 *resptr = '\0'; 299 300 while (_cups_isspace(*resval)) 301 resval ++; 302 303 for (resptr = reschoice; 304 *resval && !_cups_isspace(*resval); 305 resval ++) 306 if (resptr < (reschoice + sizeof(reschoice) - 1)) 307 *resptr++ = *resval; 308 309 *resptr = '\0'; 310 311 if (!resoption[0] || !reschoice[0]) 312 break; 313 314 /* 315 * Is this the option we are changing? 316 */ 317 318 snprintf(firstpage, sizeof(firstpage), "AP_FIRSTPAGE_%s", resoption); 319 320 if (option && 321 (!_cups_strcasecmp(resoption, option) || 322 !_cups_strcasecmp(firstpage, option) || 323 (!_cups_strcasecmp(option, "PageSize") && 324 !_cups_strcasecmp(resoption, "PageRegion")) || 325 (!_cups_strcasecmp(option, "AP_FIRSTPAGE_PageSize") && 326 !_cups_strcasecmp(resoption, "PageSize")) || 327 (!_cups_strcasecmp(option, "AP_FIRSTPAGE_PageSize") && 328 !_cups_strcasecmp(resoption, "PageRegion")) || 329 (!_cups_strcasecmp(option, "PageRegion") && 330 !_cups_strcasecmp(resoption, "PageSize")) || 331 (!_cups_strcasecmp(option, "AP_FIRSTPAGE_PageRegion") && 332 !_cups_strcasecmp(resoption, "PageSize")) || 333 (!_cups_strcasecmp(option, "AP_FIRSTPAGE_PageRegion") && 334 !_cups_strcasecmp(resoption, "PageRegion")))) 335 continue; 336 337 /* 338 * Try this choice... 339 */ 340 341 if ((test = ppd_test_constraints(ppd, resoption, reschoice, 342 num_newopts, newopts, 343 _PPD_ALL_CONSTRAINTS)) == NULL) 344 { 345 /* 346 * That worked... 347 */ 348 349 changed = 1; 350 } 351 else 352 cupsArrayDelete(test); 353 354 /* 355 * Add the option/choice from the resolver regardless of whether it 356 * worked; this makes sure that we can cascade several changes to 357 * make things resolve... 358 */ 359 360 num_newopts = cupsAddOption(resoption, reschoice, num_newopts, 361 &newopts); 362 } 363 } 364 else 365 { 366 /* 367 * Try resolving by choosing the default values for non-installable 368 * options, then by iterating through the possible choices... 369 */ 370 371 int j; /* Looping var */ 372 ppd_choice_t *cptr; /* Current choice */ 373 ppd_size_t *size; /* Current page size */ 374 375 376 for (i = consts->num_constraints, constptr = consts->constraints; 377 i > 0 && !changed; 378 i --, constptr ++) 379 { 380 /* 381 * Can't resolve by changing an installable option... 382 */ 383 384 if (constptr->installable) 385 continue; 386 387 /* 388 * Is this the option we are changing? 389 */ 390 391 if (option && 392 (!_cups_strcasecmp(constptr->option->keyword, option) || 393 (!_cups_strcasecmp(option, "PageSize") && 394 !_cups_strcasecmp(constptr->option->keyword, "PageRegion")) || 395 (!_cups_strcasecmp(option, "PageRegion") && 396 !_cups_strcasecmp(constptr->option->keyword, "PageSize")))) 397 continue; 398 399 /* 400 * Get the current option choice... 401 */ 402 403 if ((value = cupsGetOption(constptr->option->keyword, num_newopts, 404 newopts)) == NULL) 405 { 406 if (!_cups_strcasecmp(constptr->option->keyword, "PageSize") || 407 !_cups_strcasecmp(constptr->option->keyword, "PageRegion")) 408 { 409 if ((value = cupsGetOption("PageSize", num_newopts, 410 newopts)) == NULL) 411 value = cupsGetOption("PageRegion", num_newopts, newopts); 412 413 if (!value) 414 { 415 if ((size = ppdPageSize(ppd, NULL)) != NULL) 416 value = size->name; 417 else 418 value = ""; 419 } 420 } 421 else 422 { 423 marked = ppdFindMarkedChoice(ppd, constptr->option->keyword); 424 value = marked ? marked->choice : ""; 425 } 426 } 427 428 if (!_cups_strncasecmp(value, "Custom.", 7)) 429 value = "Custom"; 430 431 /* 432 * Try the default choice... 433 */ 434 435 test = NULL; 436 437 if (_cups_strcasecmp(value, constptr->option->defchoice) && 438 (test = ppd_test_constraints(ppd, constptr->option->keyword, 439 constptr->option->defchoice, 440 num_newopts, newopts, 441 _PPD_OPTION_CONSTRAINTS)) == NULL) 442 { 443 /* 444 * That worked... 445 */ 446 447 num_newopts = cupsAddOption(constptr->option->keyword, 448 constptr->option->defchoice, 449 num_newopts, &newopts); 450 changed = 1; 451 } 452 else 453 { 454 /* 455 * Try each choice instead... 456 */ 457 458 for (j = constptr->option->num_choices, 459 cptr = constptr->option->choices; 460 j > 0; 461 j --, cptr ++) 462 { 463 cupsArrayDelete(test); 464 test = NULL; 465 466 if (_cups_strcasecmp(value, cptr->choice) && 467 _cups_strcasecmp(constptr->option->defchoice, cptr->choice) && 468 _cups_strcasecmp("Custom", cptr->choice) && 469 (test = ppd_test_constraints(ppd, constptr->option->keyword, 470 cptr->choice, num_newopts, 471 newopts, 472 _PPD_OPTION_CONSTRAINTS)) == NULL) 473 { 474 /* 475 * This choice works... 476 */ 477 478 num_newopts = cupsAddOption(constptr->option->keyword, 479 cptr->choice, num_newopts, 480 &newopts); 481 changed = 1; 482 break; 483 } 484 } 485 486 cupsArrayDelete(test); 487 } 488 } 489 } 490 } 491 492 if (!changed) 493 { 494 DEBUG_puts("1cupsResolveConflicts: Unable to automatically resolve " 495 "constraint!"); 496 goto error; 497 } 498 499 cupsArrayClear(pass); 500 cupsArrayDelete(active); 501 active = NULL; 502 } 503 504 if (tries >= 100) 505 goto error; 506 507 /* 508 * Free the caller's option array... 509 */ 510 511 cupsFreeOptions(*num_options, *options); 512 513 /* 514 * If Collate is the option we are testing, add it here. Otherwise, remove 515 * any Collate option from the resolve list since the filters automatically 516 * handle manual collation... 517 */ 518 519 if (option && !_cups_strcasecmp(option, "Collate")) 520 num_newopts = cupsAddOption(option, choice, num_newopts, &newopts); 521 else 522 num_newopts = cupsRemoveOption("Collate", num_newopts, &newopts); 523 524 /* 525 * Return the new list of options to the caller... 526 */ 527 528 *num_options = num_newopts; 529 *options = newopts; 530 531 cupsArrayDelete(pass); 532 cupsArrayDelete(resolvers); 533 534 cupsArrayRestore(ppd->sorted_attrs); 535 536 DEBUG_printf(("1cupsResolveConflicts: Returning %d options:", num_newopts)); 537 #ifdef DEBUG 538 for (i = 0; i < num_newopts; i ++) 539 DEBUG_printf(("1cupsResolveConflicts: options[%d]: %s=%s", i, 540 newopts[i].name, newopts[i].value)); 541 #endif /* DEBUG */ 542 543 return (1); 544 545 /* 546 * If we get here, we failed to resolve... 547 */ 548 549 error: 550 551 cupsFreeOptions(num_newopts, newopts); 552 553 cupsArrayDelete(active); 554 cupsArrayDelete(pass); 555 cupsArrayDelete(resolvers); 556 557 cupsArrayRestore(ppd->sorted_attrs); 558 559 DEBUG_puts("1cupsResolveConflicts: Unable to resolve conflicts!"); 560 561 return (0); 562 } 563 564 565 /* 566 * 'ppdConflicts()' - Check to see if there are any conflicts among the 567 * marked option choices. 568 * 569 * The returned value is the same as returned by @link ppdMarkOption@. 570 */ 571 572 int /* O - Number of conflicts found */ 573 ppdConflicts(ppd_file_t *ppd) /* I - PPD to check */ 574 { 575 int i, /* Looping variable */ 576 conflicts; /* Number of conflicts */ 577 cups_array_t *active; /* Active conflicts */ 578 _ppd_cups_uiconsts_t *c; /* Current constraints */ 579 _ppd_cups_uiconst_t *cptr; /* Current constraint */ 580 ppd_option_t *o; /* Current option */ 581 582 583 if (!ppd) 584 return (0); 585 586 /* 587 * Clear all conflicts... 588 */ 589 590 cupsArraySave(ppd->options); 591 592 for (o = ppdFirstOption(ppd); o; o = ppdNextOption(ppd)) 593 o->conflicted = 0; 594 595 cupsArrayRestore(ppd->options); 596 597 /* 598 * Test for conflicts... 599 */ 600 601 active = ppd_test_constraints(ppd, NULL, NULL, 0, NULL, 602 _PPD_ALL_CONSTRAINTS); 603 conflicts = cupsArrayCount(active); 604 605 /* 606 * Loop through all of the UI constraints and flag any options 607 * that conflict... 608 */ 609 610 for (c = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active); 611 c; 612 c = (_ppd_cups_uiconsts_t *)cupsArrayNext(active)) 613 { 614 for (i = c->num_constraints, cptr = c->constraints; 615 i > 0; 616 i --, cptr ++) 617 cptr->option->conflicted = 1; 618 } 619 620 cupsArrayDelete(active); 621 622 /* 623 * Return the number of conflicts found... 624 */ 625 626 return (conflicts); 627 } 628 629 630 /* 631 * 'ppdInstallableConflict()' - Test whether an option choice conflicts with 632 * an installable option. 633 * 634 * This function tests whether a particular option choice is available based 635 * on constraints against options in the "InstallableOptions" group. 636 * 637 * @since CUPS 1.4/macOS 10.6@ 638 */ 639 640 int /* O - 1 if conflicting, 0 if not conflicting */ 641 ppdInstallableConflict( 642 ppd_file_t *ppd, /* I - PPD file */ 643 const char *option, /* I - Option */ 644 const char *choice) /* I - Choice */ 645 { 646 cups_array_t *active; /* Active conflicts */ 647 648 649 DEBUG_printf(("2ppdInstallableConflict(ppd=%p, option=\"%s\", choice=\"%s\")", 650 ppd, option, choice)); 651 652 /* 653 * Range check input... 654 */ 655 656 if (!ppd || !option || !choice) 657 return (0); 658 659 /* 660 * Test constraints using the new option... 661 */ 662 663 active = ppd_test_constraints(ppd, option, choice, 0, NULL, 664 _PPD_INSTALLABLE_CONSTRAINTS); 665 666 cupsArrayDelete(active); 667 668 return (active != NULL); 669 } 670 671 672 /* 673 * 'ppd_is_installable()' - Determine whether an option is in the 674 * InstallableOptions group. 675 */ 676 677 static int /* O - 1 if installable, 0 if normal */ 678 ppd_is_installable( 679 ppd_group_t *installable, /* I - InstallableOptions group */ 680 const char *name) /* I - Option name */ 681 { 682 if (installable) 683 { 684 int i; /* Looping var */ 685 ppd_option_t *option; /* Current option */ 686 687 688 for (i = installable->num_options, option = installable->options; 689 i > 0; 690 i --, option ++) 691 if (!_cups_strcasecmp(option->keyword, name)) 692 return (1); 693 } 694 695 return (0); 696 } 697 698 699 /* 700 * 'ppd_load_constraints()' - Load constraints from a PPD file. 701 */ 702 703 static void 704 ppd_load_constraints(ppd_file_t *ppd) /* I - PPD file */ 705 { 706 int i; /* Looping var */ 707 ppd_const_t *oldconst; /* Current UIConstraints data */ 708 ppd_attr_t *constattr; /* Current cupsUIConstraints attribute */ 709 _ppd_cups_uiconsts_t *consts; /* Current cupsUIConstraints data */ 710 _ppd_cups_uiconst_t *constptr; /* Current constraint */ 711 ppd_group_t *installable; /* Installable options group */ 712 const char *vptr; /* Pointer into constraint value */ 713 char option[PPD_MAX_NAME], /* Option name/MainKeyword */ 714 choice[PPD_MAX_NAME], /* Choice/OptionKeyword */ 715 *ptr; /* Pointer into option or choice */ 716 717 718 DEBUG_printf(("7ppd_load_constraints(ppd=%p)", ppd)); 719 720 /* 721 * Create an array to hold the constraint data... 722 */ 723 724 ppd->cups_uiconstraints = cupsArrayNew(NULL, NULL); 725 726 /* 727 * Find the installable options group if it exists... 728 */ 729 730 for (i = ppd->num_groups, installable = ppd->groups; 731 i > 0; 732 i --, installable ++) 733 if (!_cups_strcasecmp(installable->name, "InstallableOptions")) 734 break; 735 736 if (i <= 0) 737 installable = NULL; 738 739 /* 740 * Load old-style [Non]UIConstraints data... 741 */ 742 743 for (i = ppd->num_consts, oldconst = ppd->consts; i > 0; i --, oldconst ++) 744 { 745 /* 746 * Weed out nearby duplicates, since the PPD spec requires that you 747 * define both "*Foo foo *Bar bar" and "*Bar bar *Foo foo"... 748 */ 749 750 if (i > 1 && 751 !_cups_strcasecmp(oldconst[0].option1, oldconst[1].option2) && 752 !_cups_strcasecmp(oldconst[0].choice1, oldconst[1].choice2) && 753 !_cups_strcasecmp(oldconst[0].option2, oldconst[1].option1) && 754 !_cups_strcasecmp(oldconst[0].choice2, oldconst[1].choice1)) 755 continue; 756 757 /* 758 * Allocate memory... 759 */ 760 761 if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL) 762 { 763 DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for " 764 "UIConstraints!"); 765 return; 766 } 767 768 if ((constptr = calloc(2, sizeof(_ppd_cups_uiconst_t))) == NULL) 769 { 770 free(consts); 771 DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for " 772 "UIConstraints!"); 773 return; 774 } 775 776 /* 777 * Fill in the information... 778 */ 779 780 consts->num_constraints = 2; 781 consts->constraints = constptr; 782 783 if (!_cups_strncasecmp(oldconst->option1, "Custom", 6) && 784 !_cups_strcasecmp(oldconst->choice1, "True")) 785 { 786 constptr[0].option = ppdFindOption(ppd, oldconst->option1 + 6); 787 constptr[0].choice = ppdFindChoice(constptr[0].option, "Custom"); 788 constptr[0].installable = 0; 789 } 790 else 791 { 792 constptr[0].option = ppdFindOption(ppd, oldconst->option1); 793 constptr[0].choice = ppdFindChoice(constptr[0].option, 794 oldconst->choice1); 795 constptr[0].installable = ppd_is_installable(installable, 796 oldconst->option1); 797 } 798 799 if (!constptr[0].option || (!constptr[0].choice && oldconst->choice1[0])) 800 { 801 DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!", 802 oldconst->option1, oldconst->choice1)); 803 free(consts->constraints); 804 free(consts); 805 continue; 806 } 807 808 if (!_cups_strncasecmp(oldconst->option2, "Custom", 6) && 809 !_cups_strcasecmp(oldconst->choice2, "True")) 810 { 811 constptr[1].option = ppdFindOption(ppd, oldconst->option2 + 6); 812 constptr[1].choice = ppdFindChoice(constptr[1].option, "Custom"); 813 constptr[1].installable = 0; 814 } 815 else 816 { 817 constptr[1].option = ppdFindOption(ppd, oldconst->option2); 818 constptr[1].choice = ppdFindChoice(constptr[1].option, 819 oldconst->choice2); 820 constptr[1].installable = ppd_is_installable(installable, 821 oldconst->option2); 822 } 823 824 if (!constptr[1].option || (!constptr[1].choice && oldconst->choice2[0])) 825 { 826 DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!", 827 oldconst->option2, oldconst->choice2)); 828 free(consts->constraints); 829 free(consts); 830 continue; 831 } 832 833 consts->installable = constptr[0].installable || constptr[1].installable; 834 835 /* 836 * Add it to the constraints array... 837 */ 838 839 cupsArrayAdd(ppd->cups_uiconstraints, consts); 840 } 841 842 /* 843 * Then load new-style constraints... 844 */ 845 846 for (constattr = ppdFindAttr(ppd, "cupsUIConstraints", NULL); 847 constattr; 848 constattr = ppdFindNextAttr(ppd, "cupsUIConstraints", NULL)) 849 { 850 if (!constattr->value) 851 { 852 DEBUG_puts("8ppd_load_constraints: Bad cupsUIConstraints value!"); 853 continue; 854 } 855 856 for (i = 0, vptr = strchr(constattr->value, '*'); 857 vptr; 858 i ++, vptr = strchr(vptr + 1, '*')); 859 860 if (i == 0) 861 { 862 DEBUG_puts("8ppd_load_constraints: Bad cupsUIConstraints value!"); 863 continue; 864 } 865 866 if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL) 867 { 868 DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for " 869 "cupsUIConstraints!"); 870 return; 871 } 872 873 if ((constptr = calloc((size_t)i, sizeof(_ppd_cups_uiconst_t))) == NULL) 874 { 875 free(consts); 876 DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for " 877 "cupsUIConstraints!"); 878 return; 879 } 880 881 consts->num_constraints = i; 882 consts->constraints = constptr; 883 884 strlcpy(consts->resolver, constattr->spec, sizeof(consts->resolver)); 885 886 for (i = 0, vptr = strchr(constattr->value, '*'); 887 vptr; 888 i ++, vptr = strchr(vptr, '*'), constptr ++) 889 { 890 /* 891 * Extract "*Option Choice" or just "*Option"... 892 */ 893 894 for (vptr ++, ptr = option; *vptr && !_cups_isspace(*vptr); vptr ++) 895 if (ptr < (option + sizeof(option) - 1)) 896 *ptr++ = *vptr; 897 898 *ptr = '\0'; 899 900 while (_cups_isspace(*vptr)) 901 vptr ++; 902 903 if (*vptr == '*') 904 choice[0] = '\0'; 905 else 906 { 907 for (ptr = choice; *vptr && !_cups_isspace(*vptr); vptr ++) 908 if (ptr < (choice + sizeof(choice) - 1)) 909 *ptr++ = *vptr; 910 911 *ptr = '\0'; 912 } 913 914 if (!_cups_strncasecmp(option, "Custom", 6) && !_cups_strcasecmp(choice, "True")) 915 { 916 _cups_strcpy(option, option + 6); 917 strlcpy(choice, "Custom", sizeof(choice)); 918 } 919 920 constptr->option = ppdFindOption(ppd, option); 921 constptr->choice = ppdFindChoice(constptr->option, choice); 922 constptr->installable = ppd_is_installable(installable, option); 923 consts->installable |= constptr->installable; 924 925 if (!constptr->option || (!constptr->choice && choice[0])) 926 { 927 DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!", 928 option, choice)); 929 break; 930 } 931 } 932 933 if (!vptr) 934 cupsArrayAdd(ppd->cups_uiconstraints, consts); 935 else 936 { 937 free(consts->constraints); 938 free(consts); 939 } 940 } 941 } 942 943 944 /* 945 * 'ppd_test_constraints()' - See if any constraints are active. 946 */ 947 948 static cups_array_t * /* O - Array of active constraints */ 949 ppd_test_constraints( 950 ppd_file_t *ppd, /* I - PPD file */ 951 const char *option, /* I - Current option */ 952 const char *choice, /* I - Current choice */ 953 int num_options, /* I - Number of additional options */ 954 cups_option_t *options, /* I - Additional options */ 955 int which) /* I - Which constraints to test */ 956 { 957 int i; /* Looping var */ 958 _ppd_cups_uiconsts_t *consts; /* Current constraints */ 959 _ppd_cups_uiconst_t *constptr; /* Current constraint */ 960 ppd_choice_t key, /* Search key */ 961 *marked; /* Marked choice */ 962 cups_array_t *active = NULL; /* Active constraints */ 963 const char *value, /* Current value */ 964 *firstvalue; /* AP_FIRSTPAGE_Keyword value */ 965 char firstpage[255]; /* AP_FIRSTPAGE_Keyword string */ 966 967 968 DEBUG_printf(("7ppd_test_constraints(ppd=%p, option=\"%s\", choice=\"%s\", " 969 "num_options=%d, options=%p, which=%d)", ppd, option, choice, 970 num_options, options, which)); 971 972 if (!ppd->cups_uiconstraints) 973 ppd_load_constraints(ppd); 974 975 DEBUG_printf(("9ppd_test_constraints: %d constraints!", 976 cupsArrayCount(ppd->cups_uiconstraints))); 977 978 cupsArraySave(ppd->marked); 979 980 for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(ppd->cups_uiconstraints); 981 consts; 982 consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(ppd->cups_uiconstraints)) 983 { 984 DEBUG_printf(("9ppd_test_constraints: installable=%d, resolver=\"%s\", " 985 "num_constraints=%d option1=\"%s\", choice1=\"%s\", " 986 "option2=\"%s\", choice2=\"%s\", ...", 987 consts->installable, consts->resolver, consts->num_constraints, 988 consts->constraints[0].option->keyword, 989 consts->constraints[0].choice ? 990 consts->constraints[0].choice->choice : "", 991 consts->constraints[1].option->keyword, 992 consts->constraints[1].choice ? 993 consts->constraints[1].choice->choice : "")); 994 995 if (consts->installable && which < _PPD_INSTALLABLE_CONSTRAINTS) 996 continue; /* Skip installable option constraint */ 997 998 if (!consts->installable && which == _PPD_INSTALLABLE_CONSTRAINTS) 999 continue; /* Skip non-installable option constraint */ 1000 1001 if (which == _PPD_OPTION_CONSTRAINTS && option) 1002 { 1003 /* 1004 * Skip constraints that do not involve the current option... 1005 */ 1006 1007 for (i = consts->num_constraints, constptr = consts->constraints; 1008 i > 0; 1009 i --, constptr ++) 1010 { 1011 if (!_cups_strcasecmp(constptr->option->keyword, option)) 1012 break; 1013 1014 if (!_cups_strncasecmp(option, "AP_FIRSTPAGE_", 13) && 1015 !_cups_strcasecmp(constptr->option->keyword, option + 13)) 1016 break; 1017 } 1018 1019 if (!i) 1020 continue; 1021 } 1022 1023 DEBUG_puts("9ppd_test_constraints: Testing..."); 1024 1025 for (i = consts->num_constraints, constptr = consts->constraints; 1026 i > 0; 1027 i --, constptr ++) 1028 { 1029 DEBUG_printf(("9ppd_test_constraints: %s=%s?", constptr->option->keyword, 1030 constptr->choice ? constptr->choice->choice : "")); 1031 1032 if (constptr->choice && 1033 (!_cups_strcasecmp(constptr->option->keyword, "PageSize") || 1034 !_cups_strcasecmp(constptr->option->keyword, "PageRegion"))) 1035 { 1036 /* 1037 * PageSize and PageRegion are used depending on the selected input slot 1038 * and manual feed mode. Validate against the selected page size instead 1039 * of an individual option... 1040 */ 1041 1042 if (option && choice && 1043 (!_cups_strcasecmp(option, "PageSize") || 1044 !_cups_strcasecmp(option, "PageRegion"))) 1045 { 1046 value = choice; 1047 } 1048 else if ((value = cupsGetOption("PageSize", num_options, 1049 options)) == NULL) 1050 if ((value = cupsGetOption("PageRegion", num_options, 1051 options)) == NULL) 1052 if ((value = cupsGetOption("media", num_options, options)) == NULL) 1053 { 1054 ppd_size_t *size = ppdPageSize(ppd, NULL); 1055 1056 if (size) 1057 value = size->name; 1058 } 1059 1060 if (value && !_cups_strncasecmp(value, "Custom.", 7)) 1061 value = "Custom"; 1062 1063 if (option && choice && 1064 (!_cups_strcasecmp(option, "AP_FIRSTPAGE_PageSize") || 1065 !_cups_strcasecmp(option, "AP_FIRSTPAGE_PageRegion"))) 1066 { 1067 firstvalue = choice; 1068 } 1069 else if ((firstvalue = cupsGetOption("AP_FIRSTPAGE_PageSize", 1070 num_options, options)) == NULL) 1071 firstvalue = cupsGetOption("AP_FIRSTPAGE_PageRegion", num_options, 1072 options); 1073 1074 if (firstvalue && !_cups_strncasecmp(firstvalue, "Custom.", 7)) 1075 firstvalue = "Custom"; 1076 1077 if ((!value || _cups_strcasecmp(value, constptr->choice->choice)) && 1078 (!firstvalue || _cups_strcasecmp(firstvalue, constptr->choice->choice))) 1079 { 1080 DEBUG_puts("9ppd_test_constraints: NO"); 1081 break; 1082 } 1083 } 1084 else if (constptr->choice) 1085 { 1086 /* 1087 * Compare against the constrained choice... 1088 */ 1089 1090 if (option && choice && !_cups_strcasecmp(option, constptr->option->keyword)) 1091 { 1092 if (!_cups_strncasecmp(choice, "Custom.", 7)) 1093 value = "Custom"; 1094 else 1095 value = choice; 1096 } 1097 else if ((value = cupsGetOption(constptr->option->keyword, num_options, 1098 options)) != NULL) 1099 { 1100 if (!_cups_strncasecmp(value, "Custom.", 7)) 1101 value = "Custom"; 1102 } 1103 else if (constptr->choice->marked) 1104 value = constptr->choice->choice; 1105 else 1106 value = NULL; 1107 1108 /* 1109 * Now check AP_FIRSTPAGE_option... 1110 */ 1111 1112 snprintf(firstpage, sizeof(firstpage), "AP_FIRSTPAGE_%s", 1113 constptr->option->keyword); 1114 1115 if (option && choice && !_cups_strcasecmp(option, firstpage)) 1116 { 1117 if (!_cups_strncasecmp(choice, "Custom.", 7)) 1118 firstvalue = "Custom"; 1119 else 1120 firstvalue = choice; 1121 } 1122 else if ((firstvalue = cupsGetOption(firstpage, num_options, 1123 options)) != NULL) 1124 { 1125 if (!_cups_strncasecmp(firstvalue, "Custom.", 7)) 1126 firstvalue = "Custom"; 1127 } 1128 else 1129 firstvalue = NULL; 1130 1131 DEBUG_printf(("9ppd_test_constraints: value=%s, firstvalue=%s", value, 1132 firstvalue)); 1133 1134 if ((!value || _cups_strcasecmp(value, constptr->choice->choice)) && 1135 (!firstvalue || _cups_strcasecmp(firstvalue, constptr->choice->choice))) 1136 { 1137 DEBUG_puts("9ppd_test_constraints: NO"); 1138 break; 1139 } 1140 } 1141 else if (option && choice && 1142 !_cups_strcasecmp(option, constptr->option->keyword)) 1143 { 1144 if (!_cups_strcasecmp(choice, "None") || !_cups_strcasecmp(choice, "Off") || 1145 !_cups_strcasecmp(choice, "False")) 1146 { 1147 DEBUG_puts("9ppd_test_constraints: NO"); 1148 break; 1149 } 1150 } 1151 else if ((value = cupsGetOption(constptr->option->keyword, num_options, 1152 options)) != NULL) 1153 { 1154 if (!_cups_strcasecmp(value, "None") || !_cups_strcasecmp(value, "Off") || 1155 !_cups_strcasecmp(value, "False")) 1156 { 1157 DEBUG_puts("9ppd_test_constraints: NO"); 1158 break; 1159 } 1160 } 1161 else 1162 { 1163 key.option = constptr->option; 1164 1165 if ((marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) 1166 == NULL || 1167 (!_cups_strcasecmp(marked->choice, "None") || 1168 !_cups_strcasecmp(marked->choice, "Off") || 1169 !_cups_strcasecmp(marked->choice, "False"))) 1170 { 1171 DEBUG_puts("9ppd_test_constraints: NO"); 1172 break; 1173 } 1174 } 1175 } 1176 1177 if (i <= 0) 1178 { 1179 if (!active) 1180 active = cupsArrayNew(NULL, NULL); 1181 1182 cupsArrayAdd(active, consts); 1183 DEBUG_puts("9ppd_test_constraints: Added..."); 1184 } 1185 } 1186 1187 cupsArrayRestore(ppd->marked); 1188 1189 DEBUG_printf(("8ppd_test_constraints: Found %d active constraints!", 1190 cupsArrayCount(active))); 1191 1192 return (active); 1193 } 1194