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