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 * Licensed under Apache License v2.0. See the file "LICENSE" for more
10 * PostScript is a trademark of Adobe Systems, Inc.
14 * Include necessary headers...
17 #include "cups-private.h"
18 #include "ppd-private.h"
27 _PPD_OPTION_CONSTRAINTS
,
28 _PPD_INSTALLABLE_CONSTRAINTS
,
37 static int ppd_is_installable(ppd_group_t
*installable
,
39 static void ppd_load_constraints(ppd_file_t
*ppd
);
40 static cups_array_t
*ppd_test_constraints(ppd_file_t
*ppd
,
44 cups_option_t
*options
,
49 * 'cupsGetConflicts()' - Get a list of conflicting options in a marked PPD.
51 * This function gets a list of options that would conflict if "option" and
52 * "choice" were marked in the PPD. You would typically call this function
53 * after marking the currently selected options in the PPD in order to
54 * determine whether a new option selection would cause a conflict.
56 * The number of conflicting options are returned with "options" pointing to
57 * the conflicting options. The returned option array must be freed using
58 * @link cupsFreeOptions@.
60 * @since CUPS 1.4/macOS 10.6@
63 int /* O - Number of conflicting options */
65 ppd_file_t
*ppd
, /* I - PPD file */
66 const char *option
, /* I - Option to test */
67 const char *choice
, /* I - Choice to test */
68 cups_option_t
**options
) /* O - Conflicting options */
70 int i
, /* Looping var */
71 num_options
; /* Number of conflicting options */
72 cups_array_t
*active
; /* Active conflicts */
73 _ppd_cups_uiconsts_t
*c
; /* Current constraints */
74 _ppd_cups_uiconst_t
*cptr
; /* Current constraint */
75 ppd_choice_t
*marked
; /* Marked choice */
79 * Range check input...
85 if (!ppd
|| !option
|| !choice
|| !options
)
89 * Test for conflicts...
92 active
= ppd_test_constraints(ppd
, option
, choice
, 0, NULL
,
93 _PPD_ALL_CONSTRAINTS
);
96 * Loop through all of the UI constraints and add any options that conflict...
99 for (num_options
= 0, c
= (_ppd_cups_uiconsts_t
*)cupsArrayFirst(active
);
101 c
= (_ppd_cups_uiconsts_t
*)cupsArrayNext(active
))
103 for (i
= c
->num_constraints
, cptr
= c
->constraints
;
106 if (_cups_strcasecmp(cptr
->option
->keyword
, option
))
109 num_options
= cupsAddOption(cptr
->option
->keyword
,
110 cptr
->choice
->choice
, num_options
,
112 else if ((marked
= ppdFindMarkedChoice(ppd
,
113 cptr
->option
->keyword
)) != NULL
)
114 num_options
= cupsAddOption(cptr
->option
->keyword
, marked
->choice
,
115 num_options
, options
);
119 cupsArrayDelete(active
);
121 return (num_options
);
126 * 'cupsResolveConflicts()' - Resolve conflicts in a marked PPD.
128 * This function attempts to resolve any conflicts in a marked PPD, returning
129 * a list of option changes that are required to resolve them. On input,
130 * "num_options" and "options" contain any pending option changes that have
131 * not yet been marked, while "option" and "choice" contain the most recent
132 * selection which may or may not be in "num_options" or "options".
134 * On successful return, "num_options" and "options" are updated to contain
135 * "option" and "choice" along with any changes required to resolve conflicts
136 * specified in the PPD file and 1 is returned.
138 * If option conflicts cannot be resolved, "num_options" and "options" are not
139 * changed and 0 is returned.
141 * When resolving conflicts, @code cupsResolveConflicts@ does not consider
142 * changes to the current page size (@code media@, @code PageSize@, and
143 * @code PageRegion@) or to the most recent option specified in "option".
144 * Thus, if the only way to resolve a conflict is to change the page size
145 * or the option the user most recently changed, @code cupsResolveConflicts@
146 * will return 0 to indicate it was unable to resolve the conflicts.
148 * The @code cupsResolveConflicts@ function uses one of two sources of option
149 * constraint information. The preferred constraint information is defined by
150 * @code cupsUIConstraints@ and @code cupsUIResolver@ attributes - in this
151 * case, the PPD file provides constraint resolution actions.
153 * The backup constraint information is defined by the
154 * @code UIConstraints@ and @code NonUIConstraints@ attributes. These
155 * constraints are resolved algorithmically by first selecting the default
156 * choice for the conflicting option, then iterating over all possible choices
157 * until a non-conflicting option choice is found.
159 * @since CUPS 1.4/macOS 10.6@
162 int /* O - 1 on success, 0 on failure */
163 cupsResolveConflicts(
164 ppd_file_t
*ppd
, /* I - PPD file */
165 const char *option
, /* I - Newly selected option or @code NULL@ for none */
166 const char *choice
, /* I - Newly selected choice or @code NULL@ for none */
167 int *num_options
, /* IO - Number of additional selected options */
168 cups_option_t
**options
) /* IO - Additional selected options */
170 int i
, /* Looping var */
171 tries
, /* Number of tries */
172 num_newopts
; /* Number of new options */
173 cups_option_t
*newopts
; /* New options */
174 cups_array_t
*active
= NULL
, /* Active constraints */
175 *pass
, /* Resolvers for this pass */
176 *resolvers
, /* Resolvers we have used */
177 *test
; /* Test array for conflicts */
178 _ppd_cups_uiconsts_t
*consts
; /* Current constraints */
179 _ppd_cups_uiconst_t
*constptr
; /* Current constraint */
180 ppd_attr_t
*resolver
; /* Current resolver */
181 const char *resval
; /* Pointer into resolver value */
182 char resoption
[PPD_MAX_NAME
],
183 /* Current resolver option */
184 reschoice
[PPD_MAX_NAME
],
185 /* Current resolver choice */
186 *resptr
, /* Pointer into option/choice */
187 firstpage
[255]; /* AP_FIRSTPAGE_Keyword string */
188 const char *value
; /* Selected option value */
189 int changed
; /* Did we change anything? */
190 ppd_choice_t
*marked
; /* Marked choice */
194 * Range check input...
197 if (!ppd
|| !num_options
|| !options
|| (option
== NULL
) != (choice
== NULL
))
201 * Build a shadow option array...
207 for (i
= 0; i
< *num_options
; i
++)
208 num_newopts
= cupsAddOption((*options
)[i
].name
, (*options
)[i
].value
,
209 num_newopts
, &newopts
);
210 if (option
&& _cups_strcasecmp(option
, "Collate"))
211 num_newopts
= cupsAddOption(option
, choice
, num_newopts
, &newopts
);
214 * Loop until we have no conflicts...
217 cupsArraySave(ppd
->sorted_attrs
);
220 pass
= cupsArrayNew((cups_array_func_t
)_cups_strcasecmp
, NULL
);
223 while (tries
< 100 &&
224 (active
= ppd_test_constraints(ppd
, NULL
, NULL
, num_newopts
, newopts
,
225 _PPD_ALL_CONSTRAINTS
)) != NULL
)
230 resolvers
= cupsArrayNew((cups_array_func_t
)_cups_strcasecmp
, NULL
);
232 for (consts
= (_ppd_cups_uiconsts_t
*)cupsArrayFirst(active
), changed
= 0;
234 consts
= (_ppd_cups_uiconsts_t
*)cupsArrayNext(active
))
236 if (consts
->resolver
[0])
239 * Look up the resolver...
242 if (cupsArrayFind(pass
, consts
->resolver
))
243 continue; /* Already applied this resolver... */
245 if (cupsArrayFind(resolvers
, consts
->resolver
))
251 DEBUG_printf(("1cupsResolveConflicts: Resolver loop with %s!",
256 if ((resolver
= ppdFindAttr(ppd
, "cupsUIResolver",
257 consts
->resolver
)) == NULL
)
259 DEBUG_printf(("1cupsResolveConflicts: Resolver %s not found!",
264 if (!resolver
->value
)
266 DEBUG_printf(("1cupsResolveConflicts: Resolver %s has no value!",
272 * Add the options from the resolver...
275 cupsArrayAdd(pass
, consts
->resolver
);
276 cupsArrayAdd(resolvers
, consts
->resolver
);
278 for (resval
= resolver
->value
; *resval
&& !changed
;)
280 while (_cups_isspace(*resval
))
286 for (resval
++, resptr
= resoption
;
287 *resval
&& !_cups_isspace(*resval
);
289 if (resptr
< (resoption
+ sizeof(resoption
) - 1))
294 while (_cups_isspace(*resval
))
297 for (resptr
= reschoice
;
298 *resval
&& !_cups_isspace(*resval
);
300 if (resptr
< (reschoice
+ sizeof(reschoice
) - 1))
305 if (!resoption
[0] || !reschoice
[0])
309 * Is this the option we are changing?
312 snprintf(firstpage
, sizeof(firstpage
), "AP_FIRSTPAGE_%s", resoption
);
315 (!_cups_strcasecmp(resoption
, option
) ||
316 !_cups_strcasecmp(firstpage
, option
) ||
317 (!_cups_strcasecmp(option
, "PageSize") &&
318 !_cups_strcasecmp(resoption
, "PageRegion")) ||
319 (!_cups_strcasecmp(option
, "AP_FIRSTPAGE_PageSize") &&
320 !_cups_strcasecmp(resoption
, "PageSize")) ||
321 (!_cups_strcasecmp(option
, "AP_FIRSTPAGE_PageSize") &&
322 !_cups_strcasecmp(resoption
, "PageRegion")) ||
323 (!_cups_strcasecmp(option
, "PageRegion") &&
324 !_cups_strcasecmp(resoption
, "PageSize")) ||
325 (!_cups_strcasecmp(option
, "AP_FIRSTPAGE_PageRegion") &&
326 !_cups_strcasecmp(resoption
, "PageSize")) ||
327 (!_cups_strcasecmp(option
, "AP_FIRSTPAGE_PageRegion") &&
328 !_cups_strcasecmp(resoption
, "PageRegion"))))
335 if ((test
= ppd_test_constraints(ppd
, resoption
, reschoice
,
336 num_newopts
, newopts
,
337 _PPD_ALL_CONSTRAINTS
)) == NULL
)
346 cupsArrayDelete(test
);
349 * Add the option/choice from the resolver regardless of whether it
350 * worked; this makes sure that we can cascade several changes to
351 * make things resolve...
354 num_newopts
= cupsAddOption(resoption
, reschoice
, num_newopts
,
361 * Try resolving by choosing the default values for non-installable
362 * options, then by iterating through the possible choices...
365 int j
; /* Looping var */
366 ppd_choice_t
*cptr
; /* Current choice */
367 ppd_size_t
*size
; /* Current page size */
370 for (i
= consts
->num_constraints
, constptr
= consts
->constraints
;
375 * Can't resolve by changing an installable option...
378 if (constptr
->installable
)
382 * Is this the option we are changing?
386 (!_cups_strcasecmp(constptr
->option
->keyword
, option
) ||
387 (!_cups_strcasecmp(option
, "PageSize") &&
388 !_cups_strcasecmp(constptr
->option
->keyword
, "PageRegion")) ||
389 (!_cups_strcasecmp(option
, "PageRegion") &&
390 !_cups_strcasecmp(constptr
->option
->keyword
, "PageSize"))))
394 * Get the current option choice...
397 if ((value
= cupsGetOption(constptr
->option
->keyword
, num_newopts
,
400 if (!_cups_strcasecmp(constptr
->option
->keyword
, "PageSize") ||
401 !_cups_strcasecmp(constptr
->option
->keyword
, "PageRegion"))
403 if ((value
= cupsGetOption("PageSize", num_newopts
,
405 value
= cupsGetOption("PageRegion", num_newopts
, newopts
);
409 if ((size
= ppdPageSize(ppd
, NULL
)) != NULL
)
417 marked
= ppdFindMarkedChoice(ppd
, constptr
->option
->keyword
);
418 value
= marked
? marked
->choice
: "";
422 if (!_cups_strncasecmp(value
, "Custom.", 7))
426 * Try the default choice...
431 if (_cups_strcasecmp(value
, constptr
->option
->defchoice
) &&
432 (test
= ppd_test_constraints(ppd
, constptr
->option
->keyword
,
433 constptr
->option
->defchoice
,
434 num_newopts
, newopts
,
435 _PPD_OPTION_CONSTRAINTS
)) == NULL
)
441 num_newopts
= cupsAddOption(constptr
->option
->keyword
,
442 constptr
->option
->defchoice
,
443 num_newopts
, &newopts
);
449 * Try each choice instead...
452 for (j
= constptr
->option
->num_choices
,
453 cptr
= constptr
->option
->choices
;
457 cupsArrayDelete(test
);
460 if (_cups_strcasecmp(value
, cptr
->choice
) &&
461 _cups_strcasecmp(constptr
->option
->defchoice
, cptr
->choice
) &&
462 _cups_strcasecmp("Custom", cptr
->choice
) &&
463 (test
= ppd_test_constraints(ppd
, constptr
->option
->keyword
,
464 cptr
->choice
, num_newopts
,
466 _PPD_OPTION_CONSTRAINTS
)) == NULL
)
469 * This choice works...
472 num_newopts
= cupsAddOption(constptr
->option
->keyword
,
473 cptr
->choice
, num_newopts
,
480 cupsArrayDelete(test
);
488 DEBUG_puts("1cupsResolveConflicts: Unable to automatically resolve "
493 cupsArrayClear(pass
);
494 cupsArrayDelete(active
);
502 * Free the caller's option array...
505 cupsFreeOptions(*num_options
, *options
);
508 * If Collate is the option we are testing, add it here. Otherwise, remove
509 * any Collate option from the resolve list since the filters automatically
510 * handle manual collation...
513 if (option
&& !_cups_strcasecmp(option
, "Collate"))
514 num_newopts
= cupsAddOption(option
, choice
, num_newopts
, &newopts
);
516 num_newopts
= cupsRemoveOption("Collate", num_newopts
, &newopts
);
519 * Return the new list of options to the caller...
522 *num_options
= num_newopts
;
525 cupsArrayDelete(pass
);
526 cupsArrayDelete(resolvers
);
528 cupsArrayRestore(ppd
->sorted_attrs
);
530 DEBUG_printf(("1cupsResolveConflicts: Returning %d options:", num_newopts
));
532 for (i
= 0; i
< num_newopts
; i
++)
533 DEBUG_printf(("1cupsResolveConflicts: options[%d]: %s=%s", i
,
534 newopts
[i
].name
, newopts
[i
].value
));
540 * If we get here, we failed to resolve...
545 cupsFreeOptions(num_newopts
, newopts
);
547 cupsArrayDelete(active
);
548 cupsArrayDelete(pass
);
549 cupsArrayDelete(resolvers
);
551 cupsArrayRestore(ppd
->sorted_attrs
);
553 DEBUG_puts("1cupsResolveConflicts: Unable to resolve conflicts!");
560 * 'ppdConflicts()' - Check to see if there are any conflicts among the
561 * marked option choices.
563 * The returned value is the same as returned by @link ppdMarkOption@.
566 int /* O - Number of conflicts found */
567 ppdConflicts(ppd_file_t
*ppd
) /* I - PPD to check */
569 int i
, /* Looping variable */
570 conflicts
; /* Number of conflicts */
571 cups_array_t
*active
; /* Active conflicts */
572 _ppd_cups_uiconsts_t
*c
; /* Current constraints */
573 _ppd_cups_uiconst_t
*cptr
; /* Current constraint */
574 ppd_option_t
*o
; /* Current option */
581 * Clear all conflicts...
584 cupsArraySave(ppd
->options
);
586 for (o
= ppdFirstOption(ppd
); o
; o
= ppdNextOption(ppd
))
589 cupsArrayRestore(ppd
->options
);
592 * Test for conflicts...
595 active
= ppd_test_constraints(ppd
, NULL
, NULL
, 0, NULL
,
596 _PPD_ALL_CONSTRAINTS
);
597 conflicts
= cupsArrayCount(active
);
600 * Loop through all of the UI constraints and flag any options
604 for (c
= (_ppd_cups_uiconsts_t
*)cupsArrayFirst(active
);
606 c
= (_ppd_cups_uiconsts_t
*)cupsArrayNext(active
))
608 for (i
= c
->num_constraints
, cptr
= c
->constraints
;
611 cptr
->option
->conflicted
= 1;
614 cupsArrayDelete(active
);
617 * Return the number of conflicts found...
625 * 'ppdInstallableConflict()' - Test whether an option choice conflicts with
626 * an installable option.
628 * This function tests whether a particular option choice is available based
629 * on constraints against options in the "InstallableOptions" group.
631 * @since CUPS 1.4/macOS 10.6@
634 int /* O - 1 if conflicting, 0 if not conflicting */
635 ppdInstallableConflict(
636 ppd_file_t
*ppd
, /* I - PPD file */
637 const char *option
, /* I - Option */
638 const char *choice
) /* I - Choice */
640 cups_array_t
*active
; /* Active conflicts */
643 DEBUG_printf(("2ppdInstallableConflict(ppd=%p, option=\"%s\", choice=\"%s\")",
644 ppd
, option
, choice
));
647 * Range check input...
650 if (!ppd
|| !option
|| !choice
)
654 * Test constraints using the new option...
657 active
= ppd_test_constraints(ppd
, option
, choice
, 0, NULL
,
658 _PPD_INSTALLABLE_CONSTRAINTS
);
660 cupsArrayDelete(active
);
662 return (active
!= NULL
);
667 * 'ppd_is_installable()' - Determine whether an option is in the
668 * InstallableOptions group.
671 static int /* O - 1 if installable, 0 if normal */
673 ppd_group_t
*installable
, /* I - InstallableOptions group */
674 const char *name
) /* I - Option name */
678 int i
; /* Looping var */
679 ppd_option_t
*option
; /* Current option */
682 for (i
= installable
->num_options
, option
= installable
->options
;
685 if (!_cups_strcasecmp(option
->keyword
, name
))
694 * 'ppd_load_constraints()' - Load constraints from a PPD file.
698 ppd_load_constraints(ppd_file_t
*ppd
) /* I - PPD file */
700 int i
; /* Looping var */
701 ppd_const_t
*oldconst
; /* Current UIConstraints data */
702 ppd_attr_t
*constattr
; /* Current cupsUIConstraints attribute */
703 _ppd_cups_uiconsts_t
*consts
; /* Current cupsUIConstraints data */
704 _ppd_cups_uiconst_t
*constptr
; /* Current constraint */
705 ppd_group_t
*installable
; /* Installable options group */
706 const char *vptr
; /* Pointer into constraint value */
707 char option
[PPD_MAX_NAME
], /* Option name/MainKeyword */
708 choice
[PPD_MAX_NAME
], /* Choice/OptionKeyword */
709 *ptr
; /* Pointer into option or choice */
712 DEBUG_printf(("7ppd_load_constraints(ppd=%p)", ppd
));
715 * Create an array to hold the constraint data...
718 ppd
->cups_uiconstraints
= cupsArrayNew(NULL
, NULL
);
721 * Find the installable options group if it exists...
724 for (i
= ppd
->num_groups
, installable
= ppd
->groups
;
726 i
--, installable
++)
727 if (!_cups_strcasecmp(installable
->name
, "InstallableOptions"))
734 * Load old-style [Non]UIConstraints data...
737 for (i
= ppd
->num_consts
, oldconst
= ppd
->consts
; i
> 0; i
--, oldconst
++)
740 * Weed out nearby duplicates, since the PPD spec requires that you
741 * define both "*Foo foo *Bar bar" and "*Bar bar *Foo foo"...
745 !_cups_strcasecmp(oldconst
[0].option1
, oldconst
[1].option2
) &&
746 !_cups_strcasecmp(oldconst
[0].choice1
, oldconst
[1].choice2
) &&
747 !_cups_strcasecmp(oldconst
[0].option2
, oldconst
[1].option1
) &&
748 !_cups_strcasecmp(oldconst
[0].choice2
, oldconst
[1].choice1
))
755 if ((consts
= calloc(1, sizeof(_ppd_cups_uiconsts_t
))) == NULL
)
757 DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
762 if ((constptr
= calloc(2, sizeof(_ppd_cups_uiconst_t
))) == NULL
)
765 DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
771 * Fill in the information...
774 consts
->num_constraints
= 2;
775 consts
->constraints
= constptr
;
777 if (!_cups_strncasecmp(oldconst
->option1
, "Custom", 6) &&
778 !_cups_strcasecmp(oldconst
->choice1
, "True"))
780 constptr
[0].option
= ppdFindOption(ppd
, oldconst
->option1
+ 6);
781 constptr
[0].choice
= ppdFindChoice(constptr
[0].option
, "Custom");
782 constptr
[0].installable
= 0;
786 constptr
[0].option
= ppdFindOption(ppd
, oldconst
->option1
);
787 constptr
[0].choice
= ppdFindChoice(constptr
[0].option
,
789 constptr
[0].installable
= ppd_is_installable(installable
,
793 if (!constptr
[0].option
|| (!constptr
[0].choice
&& oldconst
->choice1
[0]))
795 DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!",
796 oldconst
->option1
, oldconst
->choice1
));
797 free(consts
->constraints
);
802 if (!_cups_strncasecmp(oldconst
->option2
, "Custom", 6) &&
803 !_cups_strcasecmp(oldconst
->choice2
, "True"))
805 constptr
[1].option
= ppdFindOption(ppd
, oldconst
->option2
+ 6);
806 constptr
[1].choice
= ppdFindChoice(constptr
[1].option
, "Custom");
807 constptr
[1].installable
= 0;
811 constptr
[1].option
= ppdFindOption(ppd
, oldconst
->option2
);
812 constptr
[1].choice
= ppdFindChoice(constptr
[1].option
,
814 constptr
[1].installable
= ppd_is_installable(installable
,
818 if (!constptr
[1].option
|| (!constptr
[1].choice
&& oldconst
->choice2
[0]))
820 DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!",
821 oldconst
->option2
, oldconst
->choice2
));
822 free(consts
->constraints
);
827 consts
->installable
= constptr
[0].installable
|| constptr
[1].installable
;
830 * Add it to the constraints array...
833 cupsArrayAdd(ppd
->cups_uiconstraints
, consts
);
837 * Then load new-style constraints...
840 for (constattr
= ppdFindAttr(ppd
, "cupsUIConstraints", NULL
);
842 constattr
= ppdFindNextAttr(ppd
, "cupsUIConstraints", NULL
))
844 if (!constattr
->value
)
846 DEBUG_puts("8ppd_load_constraints: Bad cupsUIConstraints value!");
850 for (i
= 0, vptr
= strchr(constattr
->value
, '*');
852 i
++, vptr
= strchr(vptr
+ 1, '*'));
856 DEBUG_puts("8ppd_load_constraints: Bad cupsUIConstraints value!");
860 if ((consts
= calloc(1, sizeof(_ppd_cups_uiconsts_t
))) == NULL
)
862 DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
863 "cupsUIConstraints!");
867 if ((constptr
= calloc((size_t)i
, sizeof(_ppd_cups_uiconst_t
))) == NULL
)
870 DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
871 "cupsUIConstraints!");
875 consts
->num_constraints
= i
;
876 consts
->constraints
= constptr
;
878 strlcpy(consts
->resolver
, constattr
->spec
, sizeof(consts
->resolver
));
880 for (i
= 0, vptr
= strchr(constattr
->value
, '*');
882 i
++, vptr
= strchr(vptr
, '*'), constptr
++)
885 * Extract "*Option Choice" or just "*Option"...
888 for (vptr
++, ptr
= option
; *vptr
&& !_cups_isspace(*vptr
); vptr
++)
889 if (ptr
< (option
+ sizeof(option
) - 1))
894 while (_cups_isspace(*vptr
))
901 for (ptr
= choice
; *vptr
&& !_cups_isspace(*vptr
); vptr
++)
902 if (ptr
< (choice
+ sizeof(choice
) - 1))
908 if (!_cups_strncasecmp(option
, "Custom", 6) && !_cups_strcasecmp(choice
, "True"))
910 _cups_strcpy(option
, option
+ 6);
911 strlcpy(choice
, "Custom", sizeof(choice
));
914 constptr
->option
= ppdFindOption(ppd
, option
);
915 constptr
->choice
= ppdFindChoice(constptr
->option
, choice
);
916 constptr
->installable
= ppd_is_installable(installable
, option
);
917 consts
->installable
|= constptr
->installable
;
919 if (!constptr
->option
|| (!constptr
->choice
&& choice
[0]))
921 DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!",
928 cupsArrayAdd(ppd
->cups_uiconstraints
, consts
);
931 free(consts
->constraints
);
939 * 'ppd_test_constraints()' - See if any constraints are active.
942 static cups_array_t
* /* O - Array of active constraints */
943 ppd_test_constraints(
944 ppd_file_t
*ppd
, /* I - PPD file */
945 const char *option
, /* I - Current option */
946 const char *choice
, /* I - Current choice */
947 int num_options
, /* I - Number of additional options */
948 cups_option_t
*options
, /* I - Additional options */
949 int which
) /* I - Which constraints to test */
951 int i
; /* Looping var */
952 _ppd_cups_uiconsts_t
*consts
; /* Current constraints */
953 _ppd_cups_uiconst_t
*constptr
; /* Current constraint */
954 ppd_choice_t key
, /* Search key */
955 *marked
; /* Marked choice */
956 cups_array_t
*active
= NULL
; /* Active constraints */
957 const char *value
, /* Current value */
958 *firstvalue
; /* AP_FIRSTPAGE_Keyword value */
959 char firstpage
[255]; /* AP_FIRSTPAGE_Keyword string */
962 DEBUG_printf(("7ppd_test_constraints(ppd=%p, option=\"%s\", choice=\"%s\", "
963 "num_options=%d, options=%p, which=%d)", ppd
, option
, choice
,
964 num_options
, options
, which
));
966 if (!ppd
->cups_uiconstraints
)
967 ppd_load_constraints(ppd
);
969 DEBUG_printf(("9ppd_test_constraints: %d constraints!",
970 cupsArrayCount(ppd
->cups_uiconstraints
)));
972 cupsArraySave(ppd
->marked
);
974 for (consts
= (_ppd_cups_uiconsts_t
*)cupsArrayFirst(ppd
->cups_uiconstraints
);
976 consts
= (_ppd_cups_uiconsts_t
*)cupsArrayNext(ppd
->cups_uiconstraints
))
978 DEBUG_printf(("9ppd_test_constraints: installable=%d, resolver=\"%s\", "
979 "num_constraints=%d option1=\"%s\", choice1=\"%s\", "
980 "option2=\"%s\", choice2=\"%s\", ...",
981 consts
->installable
, consts
->resolver
, consts
->num_constraints
,
982 consts
->constraints
[0].option
->keyword
,
983 consts
->constraints
[0].choice
?
984 consts
->constraints
[0].choice
->choice
: "",
985 consts
->constraints
[1].option
->keyword
,
986 consts
->constraints
[1].choice
?
987 consts
->constraints
[1].choice
->choice
: ""));
989 if (consts
->installable
&& which
< _PPD_INSTALLABLE_CONSTRAINTS
)
990 continue; /* Skip installable option constraint */
992 if (!consts
->installable
&& which
== _PPD_INSTALLABLE_CONSTRAINTS
)
993 continue; /* Skip non-installable option constraint */
995 if ((which
== _PPD_OPTION_CONSTRAINTS
|| which
== _PPD_INSTALLABLE_CONSTRAINTS
) && option
)
998 * Skip constraints that do not involve the current option...
1001 for (i
= consts
->num_constraints
, constptr
= consts
->constraints
;
1005 if (!_cups_strcasecmp(constptr
->option
->keyword
, option
))
1008 if (!_cups_strncasecmp(option
, "AP_FIRSTPAGE_", 13) &&
1009 !_cups_strcasecmp(constptr
->option
->keyword
, option
+ 13))
1017 DEBUG_puts("9ppd_test_constraints: Testing...");
1019 for (i
= consts
->num_constraints
, constptr
= consts
->constraints
;
1023 DEBUG_printf(("9ppd_test_constraints: %s=%s?", constptr
->option
->keyword
,
1024 constptr
->choice
? constptr
->choice
->choice
: ""));
1026 if (constptr
->choice
&&
1027 (!_cups_strcasecmp(constptr
->option
->keyword
, "PageSize") ||
1028 !_cups_strcasecmp(constptr
->option
->keyword
, "PageRegion")))
1031 * PageSize and PageRegion are used depending on the selected input slot
1032 * and manual feed mode. Validate against the selected page size instead
1033 * of an individual option...
1036 if (option
&& choice
&&
1037 (!_cups_strcasecmp(option
, "PageSize") ||
1038 !_cups_strcasecmp(option
, "PageRegion")))
1042 else if ((value
= cupsGetOption("PageSize", num_options
,
1044 if ((value
= cupsGetOption("PageRegion", num_options
,
1046 if ((value
= cupsGetOption("media", num_options
, options
)) == NULL
)
1048 ppd_size_t
*size
= ppdPageSize(ppd
, NULL
);
1054 if (value
&& !_cups_strncasecmp(value
, "Custom.", 7))
1057 if (option
&& choice
&&
1058 (!_cups_strcasecmp(option
, "AP_FIRSTPAGE_PageSize") ||
1059 !_cups_strcasecmp(option
, "AP_FIRSTPAGE_PageRegion")))
1061 firstvalue
= choice
;
1063 else if ((firstvalue
= cupsGetOption("AP_FIRSTPAGE_PageSize",
1064 num_options
, options
)) == NULL
)
1065 firstvalue
= cupsGetOption("AP_FIRSTPAGE_PageRegion", num_options
,
1068 if (firstvalue
&& !_cups_strncasecmp(firstvalue
, "Custom.", 7))
1069 firstvalue
= "Custom";
1071 if ((!value
|| _cups_strcasecmp(value
, constptr
->choice
->choice
)) &&
1072 (!firstvalue
|| _cups_strcasecmp(firstvalue
, constptr
->choice
->choice
)))
1074 DEBUG_puts("9ppd_test_constraints: NO");
1078 else if (constptr
->choice
)
1081 * Compare against the constrained choice...
1084 if (option
&& choice
&& !_cups_strcasecmp(option
, constptr
->option
->keyword
))
1086 if (!_cups_strncasecmp(choice
, "Custom.", 7))
1091 else if ((value
= cupsGetOption(constptr
->option
->keyword
, num_options
,
1094 if (!_cups_strncasecmp(value
, "Custom.", 7))
1097 else if (constptr
->choice
->marked
)
1098 value
= constptr
->choice
->choice
;
1103 * Now check AP_FIRSTPAGE_option...
1106 snprintf(firstpage
, sizeof(firstpage
), "AP_FIRSTPAGE_%s",
1107 constptr
->option
->keyword
);
1109 if (option
&& choice
&& !_cups_strcasecmp(option
, firstpage
))
1111 if (!_cups_strncasecmp(choice
, "Custom.", 7))
1112 firstvalue
= "Custom";
1114 firstvalue
= choice
;
1116 else if ((firstvalue
= cupsGetOption(firstpage
, num_options
,
1119 if (!_cups_strncasecmp(firstvalue
, "Custom.", 7))
1120 firstvalue
= "Custom";
1125 DEBUG_printf(("9ppd_test_constraints: value=%s, firstvalue=%s", value
,
1128 if ((!value
|| _cups_strcasecmp(value
, constptr
->choice
->choice
)) &&
1129 (!firstvalue
|| _cups_strcasecmp(firstvalue
, constptr
->choice
->choice
)))
1131 DEBUG_puts("9ppd_test_constraints: NO");
1135 else if (option
&& choice
&&
1136 !_cups_strcasecmp(option
, constptr
->option
->keyword
))
1138 if (!_cups_strcasecmp(choice
, "None") || !_cups_strcasecmp(choice
, "Off") ||
1139 !_cups_strcasecmp(choice
, "False"))
1141 DEBUG_puts("9ppd_test_constraints: NO");
1145 else if ((value
= cupsGetOption(constptr
->option
->keyword
, num_options
,
1148 if (!_cups_strcasecmp(value
, "None") || !_cups_strcasecmp(value
, "Off") ||
1149 !_cups_strcasecmp(value
, "False"))
1151 DEBUG_puts("9ppd_test_constraints: NO");
1157 key
.option
= constptr
->option
;
1159 if ((marked
= (ppd_choice_t
*)cupsArrayFind(ppd
->marked
, &key
))
1161 (!_cups_strcasecmp(marked
->choice
, "None") ||
1162 !_cups_strcasecmp(marked
->choice
, "Off") ||
1163 !_cups_strcasecmp(marked
->choice
, "False")))
1165 DEBUG_puts("9ppd_test_constraints: NO");
1174 active
= cupsArrayNew(NULL
, NULL
);
1176 cupsArrayAdd(active
, consts
);
1177 DEBUG_puts("9ppd_test_constraints: Added...");
1181 cupsArrayRestore(ppd
->marked
);
1183 DEBUG_printf(("8ppd_test_constraints: Found %d active constraints!",
1184 cupsArrayCount(active
)));