2 * Option conflict management routines for CUPS.
4 * Copyright 2007-2018 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
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/".
13 * PostScript is a trademark of Adobe Systems, Inc.
15 * This file is subject to the Apple OS-Developed Software exception.
19 * Include necessary headers...
22 #include "cups-private.h"
23 #include "ppd-private.h"
32 _PPD_OPTION_CONSTRAINTS
,
33 _PPD_INSTALLABLE_CONSTRAINTS
,
42 static int ppd_is_installable(ppd_group_t
*installable
,
44 static void ppd_load_constraints(ppd_file_t
*ppd
);
45 static cups_array_t
*ppd_test_constraints(ppd_file_t
*ppd
,
49 cups_option_t
*options
,
54 * 'cupsGetConflicts()' - Get a list of conflicting options in a marked PPD.
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.
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@.
65 * @since CUPS 1.4/macOS 10.6@
68 int /* O - Number of conflicting options */
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 */
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 */
84 * Range check input...
90 if (!ppd
|| !option
|| !choice
|| !options
)
94 * Test for conflicts...
97 active
= ppd_test_constraints(ppd
, option
, choice
, 0, NULL
,
98 _PPD_ALL_CONSTRAINTS
);
101 * Loop through all of the UI constraints and add any options that conflict...
104 for (num_options
= 0, c
= (_ppd_cups_uiconsts_t
*)cupsArrayFirst(active
);
106 c
= (_ppd_cups_uiconsts_t
*)cupsArrayNext(active
))
108 for (i
= c
->num_constraints
, cptr
= c
->constraints
;
111 if (_cups_strcasecmp(cptr
->option
->keyword
, option
))
114 num_options
= cupsAddOption(cptr
->option
->keyword
,
115 cptr
->choice
->choice
, num_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
);
124 cupsArrayDelete(active
);
126 return (num_options
);
131 * 'cupsResolveConflicts()' - Resolve conflicts in a marked PPD.
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".
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.
143 * If option conflicts cannot be resolved, "num_options" and "options" are not
144 * changed and 0 is returned.
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.
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.
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.
164 * @since CUPS 1.4/macOS 10.6@
167 int /* O - 1 on success, 0 on failure */
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 */
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 */
199 * Range check input...
202 if (!ppd
|| !num_options
|| !options
|| (option
== NULL
) != (choice
== NULL
))
206 * Build a shadow option array...
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
);
219 * Loop until we have no conflicts...
222 cupsArraySave(ppd
->sorted_attrs
);
225 pass
= cupsArrayNew((cups_array_func_t
)_cups_strcasecmp
, NULL
);
228 while (tries
< 100 &&
229 (active
= ppd_test_constraints(ppd
, NULL
, NULL
, num_newopts
, newopts
,
230 _PPD_ALL_CONSTRAINTS
)) != NULL
)
235 resolvers
= cupsArrayNew((cups_array_func_t
)_cups_strcasecmp
, NULL
);
237 for (consts
= (_ppd_cups_uiconsts_t
*)cupsArrayFirst(active
), changed
= 0;
239 consts
= (_ppd_cups_uiconsts_t
*)cupsArrayNext(active
))
241 if (consts
->resolver
[0])
244 * Look up the resolver...
247 if (cupsArrayFind(pass
, consts
->resolver
))
248 continue; /* Already applied this resolver... */
250 if (cupsArrayFind(resolvers
, consts
->resolver
))
256 DEBUG_printf(("1cupsResolveConflicts: Resolver loop with %s!",
261 if ((resolver
= ppdFindAttr(ppd
, "cupsUIResolver",
262 consts
->resolver
)) == NULL
)
264 DEBUG_printf(("1cupsResolveConflicts: Resolver %s not found!",
269 if (!resolver
->value
)
271 DEBUG_printf(("1cupsResolveConflicts: Resolver %s has no value!",
277 * Add the options from the resolver...
280 cupsArrayAdd(pass
, consts
->resolver
);
281 cupsArrayAdd(resolvers
, consts
->resolver
);
283 for (resval
= resolver
->value
; *resval
&& !changed
;)
285 while (_cups_isspace(*resval
))
291 for (resval
++, resptr
= resoption
;
292 *resval
&& !_cups_isspace(*resval
);
294 if (resptr
< (resoption
+ sizeof(resoption
) - 1))
299 while (_cups_isspace(*resval
))
302 for (resptr
= reschoice
;
303 *resval
&& !_cups_isspace(*resval
);
305 if (resptr
< (reschoice
+ sizeof(reschoice
) - 1))
310 if (!resoption
[0] || !reschoice
[0])
314 * Is this the option we are changing?
317 snprintf(firstpage
, sizeof(firstpage
), "AP_FIRSTPAGE_%s", resoption
);
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"))))
340 if ((test
= ppd_test_constraints(ppd
, resoption
, reschoice
,
341 num_newopts
, newopts
,
342 _PPD_ALL_CONSTRAINTS
)) == NULL
)
351 cupsArrayDelete(test
);
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...
359 num_newopts
= cupsAddOption(resoption
, reschoice
, num_newopts
,
366 * Try resolving by choosing the default values for non-installable
367 * options, then by iterating through the possible choices...
370 int j
; /* Looping var */
371 ppd_choice_t
*cptr
; /* Current choice */
372 ppd_size_t
*size
; /* Current page size */
375 for (i
= consts
->num_constraints
, constptr
= consts
->constraints
;
380 * Can't resolve by changing an installable option...
383 if (constptr
->installable
)
387 * Is this the option we are changing?
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"))))
399 * Get the current option choice...
402 if ((value
= cupsGetOption(constptr
->option
->keyword
, num_newopts
,
405 if (!_cups_strcasecmp(constptr
->option
->keyword
, "PageSize") ||
406 !_cups_strcasecmp(constptr
->option
->keyword
, "PageRegion"))
408 if ((value
= cupsGetOption("PageSize", num_newopts
,
410 value
= cupsGetOption("PageRegion", num_newopts
, newopts
);
414 if ((size
= ppdPageSize(ppd
, NULL
)) != NULL
)
422 marked
= ppdFindMarkedChoice(ppd
, constptr
->option
->keyword
);
423 value
= marked
? marked
->choice
: "";
427 if (!_cups_strncasecmp(value
, "Custom.", 7))
431 * Try the default choice...
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
)
446 num_newopts
= cupsAddOption(constptr
->option
->keyword
,
447 constptr
->option
->defchoice
,
448 num_newopts
, &newopts
);
454 * Try each choice instead...
457 for (j
= constptr
->option
->num_choices
,
458 cptr
= constptr
->option
->choices
;
462 cupsArrayDelete(test
);
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
,
471 _PPD_OPTION_CONSTRAINTS
)) == NULL
)
474 * This choice works...
477 num_newopts
= cupsAddOption(constptr
->option
->keyword
,
478 cptr
->choice
, num_newopts
,
485 cupsArrayDelete(test
);
493 DEBUG_puts("1cupsResolveConflicts: Unable to automatically resolve "
498 cupsArrayClear(pass
);
499 cupsArrayDelete(active
);
507 * Free the caller's option array...
510 cupsFreeOptions(*num_options
, *options
);
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...
518 if (option
&& !_cups_strcasecmp(option
, "Collate"))
519 num_newopts
= cupsAddOption(option
, choice
, num_newopts
, &newopts
);
521 num_newopts
= cupsRemoveOption("Collate", num_newopts
, &newopts
);
524 * Return the new list of options to the caller...
527 *num_options
= num_newopts
;
530 cupsArrayDelete(pass
);
531 cupsArrayDelete(resolvers
);
533 cupsArrayRestore(ppd
->sorted_attrs
);
535 DEBUG_printf(("1cupsResolveConflicts: Returning %d options:", num_newopts
));
537 for (i
= 0; i
< num_newopts
; i
++)
538 DEBUG_printf(("1cupsResolveConflicts: options[%d]: %s=%s", i
,
539 newopts
[i
].name
, newopts
[i
].value
));
545 * If we get here, we failed to resolve...
550 cupsFreeOptions(num_newopts
, newopts
);
552 cupsArrayDelete(active
);
553 cupsArrayDelete(pass
);
554 cupsArrayDelete(resolvers
);
556 cupsArrayRestore(ppd
->sorted_attrs
);
558 DEBUG_puts("1cupsResolveConflicts: Unable to resolve conflicts!");
565 * 'ppdConflicts()' - Check to see if there are any conflicts among the
566 * marked option choices.
568 * The returned value is the same as returned by @link ppdMarkOption@.
571 int /* O - Number of conflicts found */
572 ppdConflicts(ppd_file_t
*ppd
) /* I - PPD to check */
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 */
586 * Clear all conflicts...
589 cupsArraySave(ppd
->options
);
591 for (o
= ppdFirstOption(ppd
); o
; o
= ppdNextOption(ppd
))
594 cupsArrayRestore(ppd
->options
);
597 * Test for conflicts...
600 active
= ppd_test_constraints(ppd
, NULL
, NULL
, 0, NULL
,
601 _PPD_ALL_CONSTRAINTS
);
602 conflicts
= cupsArrayCount(active
);
605 * Loop through all of the UI constraints and flag any options
609 for (c
= (_ppd_cups_uiconsts_t
*)cupsArrayFirst(active
);
611 c
= (_ppd_cups_uiconsts_t
*)cupsArrayNext(active
))
613 for (i
= c
->num_constraints
, cptr
= c
->constraints
;
616 cptr
->option
->conflicted
= 1;
619 cupsArrayDelete(active
);
622 * Return the number of conflicts found...
630 * 'ppdInstallableConflict()' - Test whether an option choice conflicts with
631 * an installable option.
633 * This function tests whether a particular option choice is available based
634 * on constraints against options in the "InstallableOptions" group.
636 * @since CUPS 1.4/macOS 10.6@
639 int /* O - 1 if conflicting, 0 if not conflicting */
640 ppdInstallableConflict(
641 ppd_file_t
*ppd
, /* I - PPD file */
642 const char *option
, /* I - Option */
643 const char *choice
) /* I - Choice */
645 cups_array_t
*active
; /* Active conflicts */
648 DEBUG_printf(("2ppdInstallableConflict(ppd=%p, option=\"%s\", choice=\"%s\")",
649 ppd
, option
, choice
));
652 * Range check input...
655 if (!ppd
|| !option
|| !choice
)
659 * Test constraints using the new option...
662 active
= ppd_test_constraints(ppd
, option
, choice
, 0, NULL
,
663 _PPD_INSTALLABLE_CONSTRAINTS
);
665 cupsArrayDelete(active
);
667 return (active
!= NULL
);
672 * 'ppd_is_installable()' - Determine whether an option is in the
673 * InstallableOptions group.
676 static int /* O - 1 if installable, 0 if normal */
678 ppd_group_t
*installable
, /* I - InstallableOptions group */
679 const char *name
) /* I - Option name */
683 int i
; /* Looping var */
684 ppd_option_t
*option
; /* Current option */
687 for (i
= installable
->num_options
, option
= installable
->options
;
690 if (!_cups_strcasecmp(option
->keyword
, name
))
699 * 'ppd_load_constraints()' - Load constraints from a PPD file.
703 ppd_load_constraints(ppd_file_t
*ppd
) /* I - PPD file */
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 */
717 DEBUG_printf(("7ppd_load_constraints(ppd=%p)", ppd
));
720 * Create an array to hold the constraint data...
723 ppd
->cups_uiconstraints
= cupsArrayNew(NULL
, NULL
);
726 * Find the installable options group if it exists...
729 for (i
= ppd
->num_groups
, installable
= ppd
->groups
;
731 i
--, installable
++)
732 if (!_cups_strcasecmp(installable
->name
, "InstallableOptions"))
739 * Load old-style [Non]UIConstraints data...
742 for (i
= ppd
->num_consts
, oldconst
= ppd
->consts
; i
> 0; i
--, oldconst
++)
745 * Weed out nearby duplicates, since the PPD spec requires that you
746 * define both "*Foo foo *Bar bar" and "*Bar bar *Foo foo"...
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
))
760 if ((consts
= calloc(1, sizeof(_ppd_cups_uiconsts_t
))) == NULL
)
762 DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
767 if ((constptr
= calloc(2, sizeof(_ppd_cups_uiconst_t
))) == NULL
)
770 DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
776 * Fill in the information...
779 consts
->num_constraints
= 2;
780 consts
->constraints
= constptr
;
782 if (!_cups_strncasecmp(oldconst
->option1
, "Custom", 6) &&
783 !_cups_strcasecmp(oldconst
->choice1
, "True"))
785 constptr
[0].option
= ppdFindOption(ppd
, oldconst
->option1
+ 6);
786 constptr
[0].choice
= ppdFindChoice(constptr
[0].option
, "Custom");
787 constptr
[0].installable
= 0;
791 constptr
[0].option
= ppdFindOption(ppd
, oldconst
->option1
);
792 constptr
[0].choice
= ppdFindChoice(constptr
[0].option
,
794 constptr
[0].installable
= ppd_is_installable(installable
,
798 if (!constptr
[0].option
|| (!constptr
[0].choice
&& oldconst
->choice1
[0]))
800 DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!",
801 oldconst
->option1
, oldconst
->choice1
));
802 free(consts
->constraints
);
807 if (!_cups_strncasecmp(oldconst
->option2
, "Custom", 6) &&
808 !_cups_strcasecmp(oldconst
->choice2
, "True"))
810 constptr
[1].option
= ppdFindOption(ppd
, oldconst
->option2
+ 6);
811 constptr
[1].choice
= ppdFindChoice(constptr
[1].option
, "Custom");
812 constptr
[1].installable
= 0;
816 constptr
[1].option
= ppdFindOption(ppd
, oldconst
->option2
);
817 constptr
[1].choice
= ppdFindChoice(constptr
[1].option
,
819 constptr
[1].installable
= ppd_is_installable(installable
,
823 if (!constptr
[1].option
|| (!constptr
[1].choice
&& oldconst
->choice2
[0]))
825 DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!",
826 oldconst
->option2
, oldconst
->choice2
));
827 free(consts
->constraints
);
832 consts
->installable
= constptr
[0].installable
|| constptr
[1].installable
;
835 * Add it to the constraints array...
838 cupsArrayAdd(ppd
->cups_uiconstraints
, consts
);
842 * Then load new-style constraints...
845 for (constattr
= ppdFindAttr(ppd
, "cupsUIConstraints", NULL
);
847 constattr
= ppdFindNextAttr(ppd
, "cupsUIConstraints", NULL
))
849 if (!constattr
->value
)
851 DEBUG_puts("8ppd_load_constraints: Bad cupsUIConstraints value!");
855 for (i
= 0, vptr
= strchr(constattr
->value
, '*');
857 i
++, vptr
= strchr(vptr
+ 1, '*'));
861 DEBUG_puts("8ppd_load_constraints: Bad cupsUIConstraints value!");
865 if ((consts
= calloc(1, sizeof(_ppd_cups_uiconsts_t
))) == NULL
)
867 DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
868 "cupsUIConstraints!");
872 if ((constptr
= calloc((size_t)i
, sizeof(_ppd_cups_uiconst_t
))) == NULL
)
875 DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
876 "cupsUIConstraints!");
880 consts
->num_constraints
= i
;
881 consts
->constraints
= constptr
;
883 strlcpy(consts
->resolver
, constattr
->spec
, sizeof(consts
->resolver
));
885 for (i
= 0, vptr
= strchr(constattr
->value
, '*');
887 i
++, vptr
= strchr(vptr
, '*'), constptr
++)
890 * Extract "*Option Choice" or just "*Option"...
893 for (vptr
++, ptr
= option
; *vptr
&& !_cups_isspace(*vptr
); vptr
++)
894 if (ptr
< (option
+ sizeof(option
) - 1))
899 while (_cups_isspace(*vptr
))
906 for (ptr
= choice
; *vptr
&& !_cups_isspace(*vptr
); vptr
++)
907 if (ptr
< (choice
+ sizeof(choice
) - 1))
913 if (!_cups_strncasecmp(option
, "Custom", 6) && !_cups_strcasecmp(choice
, "True"))
915 _cups_strcpy(option
, option
+ 6);
916 strlcpy(choice
, "Custom", sizeof(choice
));
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
;
924 if (!constptr
->option
|| (!constptr
->choice
&& choice
[0]))
926 DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!",
933 cupsArrayAdd(ppd
->cups_uiconstraints
, consts
);
936 free(consts
->constraints
);
944 * 'ppd_test_constraints()' - See if any constraints are active.
947 static cups_array_t
* /* O - Array of active constraints */
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 */
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 */
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
));
971 if (!ppd
->cups_uiconstraints
)
972 ppd_load_constraints(ppd
);
974 DEBUG_printf(("9ppd_test_constraints: %d constraints!",
975 cupsArrayCount(ppd
->cups_uiconstraints
)));
977 cupsArraySave(ppd
->marked
);
979 for (consts
= (_ppd_cups_uiconsts_t
*)cupsArrayFirst(ppd
->cups_uiconstraints
);
981 consts
= (_ppd_cups_uiconsts_t
*)cupsArrayNext(ppd
->cups_uiconstraints
))
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
: ""));
994 if (consts
->installable
&& which
< _PPD_INSTALLABLE_CONSTRAINTS
)
995 continue; /* Skip installable option constraint */
997 if (!consts
->installable
&& which
== _PPD_INSTALLABLE_CONSTRAINTS
)
998 continue; /* Skip non-installable option constraint */
1000 if ((which
== _PPD_OPTION_CONSTRAINTS
|| which
== _PPD_INSTALLABLE_CONSTRAINTS
) && option
)
1003 * Skip constraints that do not involve the current option...
1006 for (i
= consts
->num_constraints
, constptr
= consts
->constraints
;
1010 if (!_cups_strcasecmp(constptr
->option
->keyword
, option
))
1013 if (!_cups_strncasecmp(option
, "AP_FIRSTPAGE_", 13) &&
1014 !_cups_strcasecmp(constptr
->option
->keyword
, option
+ 13))
1022 DEBUG_puts("9ppd_test_constraints: Testing...");
1024 for (i
= consts
->num_constraints
, constptr
= consts
->constraints
;
1028 DEBUG_printf(("9ppd_test_constraints: %s=%s?", constptr
->option
->keyword
,
1029 constptr
->choice
? constptr
->choice
->choice
: ""));
1031 if (constptr
->choice
&&
1032 (!_cups_strcasecmp(constptr
->option
->keyword
, "PageSize") ||
1033 !_cups_strcasecmp(constptr
->option
->keyword
, "PageRegion")))
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...
1041 if (option
&& choice
&&
1042 (!_cups_strcasecmp(option
, "PageSize") ||
1043 !_cups_strcasecmp(option
, "PageRegion")))
1047 else if ((value
= cupsGetOption("PageSize", num_options
,
1049 if ((value
= cupsGetOption("PageRegion", num_options
,
1051 if ((value
= cupsGetOption("media", num_options
, options
)) == NULL
)
1053 ppd_size_t
*size
= ppdPageSize(ppd
, NULL
);
1059 if (value
&& !_cups_strncasecmp(value
, "Custom.", 7))
1062 if (option
&& choice
&&
1063 (!_cups_strcasecmp(option
, "AP_FIRSTPAGE_PageSize") ||
1064 !_cups_strcasecmp(option
, "AP_FIRSTPAGE_PageRegion")))
1066 firstvalue
= choice
;
1068 else if ((firstvalue
= cupsGetOption("AP_FIRSTPAGE_PageSize",
1069 num_options
, options
)) == NULL
)
1070 firstvalue
= cupsGetOption("AP_FIRSTPAGE_PageRegion", num_options
,
1073 if (firstvalue
&& !_cups_strncasecmp(firstvalue
, "Custom.", 7))
1074 firstvalue
= "Custom";
1076 if ((!value
|| _cups_strcasecmp(value
, constptr
->choice
->choice
)) &&
1077 (!firstvalue
|| _cups_strcasecmp(firstvalue
, constptr
->choice
->choice
)))
1079 DEBUG_puts("9ppd_test_constraints: NO");
1083 else if (constptr
->choice
)
1086 * Compare against the constrained choice...
1089 if (option
&& choice
&& !_cups_strcasecmp(option
, constptr
->option
->keyword
))
1091 if (!_cups_strncasecmp(choice
, "Custom.", 7))
1096 else if ((value
= cupsGetOption(constptr
->option
->keyword
, num_options
,
1099 if (!_cups_strncasecmp(value
, "Custom.", 7))
1102 else if (constptr
->choice
->marked
)
1103 value
= constptr
->choice
->choice
;
1108 * Now check AP_FIRSTPAGE_option...
1111 snprintf(firstpage
, sizeof(firstpage
), "AP_FIRSTPAGE_%s",
1112 constptr
->option
->keyword
);
1114 if (option
&& choice
&& !_cups_strcasecmp(option
, firstpage
))
1116 if (!_cups_strncasecmp(choice
, "Custom.", 7))
1117 firstvalue
= "Custom";
1119 firstvalue
= choice
;
1121 else if ((firstvalue
= cupsGetOption(firstpage
, num_options
,
1124 if (!_cups_strncasecmp(firstvalue
, "Custom.", 7))
1125 firstvalue
= "Custom";
1130 DEBUG_printf(("9ppd_test_constraints: value=%s, firstvalue=%s", value
,
1133 if ((!value
|| _cups_strcasecmp(value
, constptr
->choice
->choice
)) &&
1134 (!firstvalue
|| _cups_strcasecmp(firstvalue
, constptr
->choice
->choice
)))
1136 DEBUG_puts("9ppd_test_constraints: NO");
1140 else if (option
&& choice
&&
1141 !_cups_strcasecmp(option
, constptr
->option
->keyword
))
1143 if (!_cups_strcasecmp(choice
, "None") || !_cups_strcasecmp(choice
, "Off") ||
1144 !_cups_strcasecmp(choice
, "False"))
1146 DEBUG_puts("9ppd_test_constraints: NO");
1150 else if ((value
= cupsGetOption(constptr
->option
->keyword
, num_options
,
1153 if (!_cups_strcasecmp(value
, "None") || !_cups_strcasecmp(value
, "Off") ||
1154 !_cups_strcasecmp(value
, "False"))
1156 DEBUG_puts("9ppd_test_constraints: NO");
1162 key
.option
= constptr
->option
;
1164 if ((marked
= (ppd_choice_t
*)cupsArrayFind(ppd
->marked
, &key
))
1166 (!_cups_strcasecmp(marked
->choice
, "None") ||
1167 !_cups_strcasecmp(marked
->choice
, "Off") ||
1168 !_cups_strcasecmp(marked
->choice
, "False")))
1170 DEBUG_puts("9ppd_test_constraints: NO");
1179 active
= cupsArrayNew(NULL
, NULL
);
1181 cupsArrayAdd(active
, consts
);
1182 DEBUG_puts("9ppd_test_constraints: Added...");
1186 cupsArrayRestore(ppd
->marked
);
1188 DEBUG_printf(("8ppd_test_constraints: Found %d active constraints!",
1189 cupsArrayCount(active
)));