Home | History | Annotate | Download | only in cups
      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