4 * Option marking routines for the Common UNIX Printing System (CUPS).
6 * Copyright 2007-2008 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * PostScript is a trademark of Adobe Systems, Inc.
17 * This file is subject to the Apple OS-Developed Software exception.
21 * ppdConflicts() - Check to see if there are any conflicts among
22 * the marked option choices.
23 * ppdInstallableConflict() - Test whether an option choice conflicts with an
25 * cupsResolveConflicts() - Resolve conflicts in a marked PPD.
26 * ppd_is_installable() - Determine whether an option is in the
27 * InstallableOptions group.
28 * ppd_load_constraints() - Load constraints from a PPD file.
29 * ppd_test_constraints() - See if any constraints are active.
33 * Include necessary headers...
36 #include "ppd-private.h"
47 _PPD_NORMAL_CONSTRAINTS
,
48 _PPD_INSTALLABLE_CONSTRAINTS
,
57 static int ppd_is_installable(ppd_group_t
*installable
,
59 static void ppd_load_constraints(ppd_file_t
*ppd
);
60 static cups_array_t
*ppd_test_constraints(ppd_file_t
*ppd
, int num_options
,
61 cups_option_t
*options
,
66 * 'ppdConflicts()' - Check to see if there are any conflicts among the
67 * marked option choices.
69 * The returned value is the same as returned by @link ppdMarkOption@.
72 int /* O - Number of conflicts found */
73 ppdConflicts(ppd_file_t
*ppd
) /* I - PPD to check */
75 int i
, /* Looping variable */
76 conflicts
; /* Number of conflicts */
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_option_t
*o
; /* Current option */
87 * Clear all conflicts...
90 for (o
= ppdFirstOption(ppd
); o
; o
= ppdNextOption(ppd
))
94 * Test for conflicts...
97 active
= ppd_test_constraints(ppd
, 0, NULL
, _PPD_ALL_CONSTRAINTS
);
98 conflicts
= cupsArrayCount(active
);
101 * Loop through all of the UI constraints and flag any options
105 for (c
= (_ppd_cups_uiconsts_t
*)cupsArrayFirst(active
);
107 c
= (_ppd_cups_uiconsts_t
*)cupsArrayNext(active
))
109 for (i
= c
->num_constraints
, cptr
= c
->constraints
;
112 cptr
->option
->conflicted
= 1;
115 cupsArrayDelete(active
);
118 * Return the number of conflicts found...
126 * 'ppdInstallableConflict()' - Test whether an option choice conflicts with
127 * an installable option.
129 * This function tests whether a particular option choice is available based
130 * on constraints against options in the "InstallableOptions" group.
135 int /* O - 1 if conflicting, 0 if not conflicting */
136 ppdInstallableConflict(
137 ppd_file_t
*ppd
, /* I - PPD file */
138 const char *option
, /* I - Option */
139 const char *choice
) /* I - Choice */
141 cups_array_t
*active
; /* Active conflicts */
142 cups_option_t test
; /* Test against this option */
146 * Range check input...
149 if (!ppd
|| !option
|| !choice
)
153 * Test constraints using the new option...
156 test
.name
= (char *)option
;
157 test
.value
= (char *)choice
;
158 active
= ppd_test_constraints(ppd
, 1, &test
,
159 _PPD_INSTALLABLE_CONSTRAINTS
);
161 cupsArrayDelete(active
);
163 return (active
!= NULL
);
168 * 'cupsResolveConflicts()' - Resolve conflicts in a marked PPD.
170 * This function attempts to resolve any conflicts in a marked PPD, returning
171 * a list of option changes that are required to resolve any conflicts. On
172 * input, "num_options" and "options" contain any pending option changes that
173 * have not yet been marked, while "option" and "choice" contain the most recent
174 * selection which may or may not be in "num_options" or "options".
176 * On successful return, "num_options" and "options" are updated to contain
177 * "option" and "choice" along with any changes required to resolve conflicts
178 * specified in the PPD file. If option conflicts cannot be resolved,
179 * "num_options" and "options" are not changed.
181 * @code ppdResolveConflicts@ uses one of two sources of option constraint
182 * information. The preferred constraint information is defined by
183 * @code cupsUIConstraints@ and @code cupsUIResolver@ attributes - in this
184 * case, the PPD file provides constraint resolution actions. In this case,
185 * it should not be possible for @ppdResolveConflicts@ to fail, however it
186 * will do so if a resolver loop is detected.
188 * The backup constraint infomration is defined by the
189 * @code UIConstraints@ and @code NonUIConstraints@ attributes. These
190 * constraints are resolved algorithmically by selecting the default choice
191 * for the conflicting option. Unfortunately, this method is far more likely
197 int /* O - 1 on success, 0 on failure */
198 cupsResolveConflicts(
199 ppd_file_t
*ppd
, /* I - PPD file */
200 const char *option
, /* I - Newly selected option or @code NULL@ for none */
201 const char *choice
, /* I - Newly selected choice or @code NULL@ for none */
202 int *num_options
, /* IO - Number of additional selected options */
203 cups_option_t
**options
) /* IO - Additional selected options */
205 int i
, /* Looping var */
206 num_newopts
; /* Number of new options */
207 cups_option_t
*newopts
; /* New options */
208 cups_array_t
*active
, /* Active constraints */
209 *pass
, /* Resolvers for this pass */
210 *resolvers
; /* Resolvers we have used */
211 _ppd_cups_uiconsts_t
*consts
; /* Current constraints */
212 _ppd_cups_uiconst_t
*constptr
; /* Current constraint */
213 ppd_attr_t
*resolver
; /* Current resolver */
214 const char *value
; /* Selected option value */
215 int changed
; /* Did we change anything? */
216 ppd_choice_t
*marked
; /* Marked choice */
217 ppd_option_t
*ignored
; /* Ignored option */
221 * Range check input...
224 if (!ppd
|| !num_options
|| !options
|| (option
== NULL
) != (choice
== NULL
))
228 * Build a shadow option array...
234 for (i
= 0; i
< *num_options
; i
++)
235 num_newopts
= cupsAddOption((*options
)[i
].name
, (*options
)[i
].value
,
236 num_newopts
, &newopts
);
237 if (option
&& strcasecmp(option
, "Collate"))
238 num_newopts
= cupsAddOption(option
, choice
, num_newopts
, &newopts
);
241 * Loop until we have no conflicts...
244 cupsArraySave(ppd
->sorted_attrs
);
247 pass
= cupsArrayNew((cups_array_func_t
)strcasecmp
, NULL
);
249 while ((active
= ppd_test_constraints(ppd
, num_newopts
, newopts
,
250 _PPD_ALL_CONSTRAINTS
)) != NULL
)
253 resolvers
= cupsArrayNew((cups_array_func_t
)strcasecmp
, NULL
);
255 for (consts
= (_ppd_cups_uiconsts_t
*)cupsArrayFirst(active
), changed
= 0;
257 consts
= (_ppd_cups_uiconsts_t
*)cupsArrayNext(active
))
259 if (consts
->resolver
[0])
262 * Look up the resolver...
265 if (cupsArrayFind(pass
, consts
->resolver
))
266 continue; /* Already applied this resolver... */
268 if (cupsArrayFind(resolvers
, consts
->resolver
))
274 DEBUG_printf(("ppdResolveConflicts: Resolver loop with %s!\n",
279 if ((resolver
= ppdFindAttr(ppd
, "cupsUIResolver",
280 consts
->resolver
)) == NULL
)
282 DEBUG_printf(("ppdResolveConflicts: Resolver %s not found!\n",
287 if (!resolver
->value
)
289 DEBUG_printf(("ppdResolveConflicts: Resolver %s has no value!\n",
295 * Add the options from the resolver...
298 cupsArrayAdd(pass
, consts
->resolver
);
299 cupsArrayAdd(resolvers
, consts
->resolver
);
301 num_newopts
= _ppdParseOptions(resolver
->value
, num_newopts
, &newopts
);
307 * Try resolving by choosing the default values for non-installable
311 for (i
= consts
->num_constraints
, constptr
= consts
->constraints
,
316 if (constptr
->installable
||
317 !strcasecmp(constptr
->option
->keyword
, "PageSize") ||
318 !strcasecmp(constptr
->option
->keyword
, "PageRegion"))
321 if (option
&& !strcasecmp(constptr
->option
->keyword
, option
))
323 ignored
= constptr
->option
;
327 if ((value
= cupsGetOption(constptr
->option
->keyword
, num_newopts
,
330 marked
= ppdFindMarkedChoice(ppd
, constptr
->option
->keyword
);
331 value
= marked
? marked
->choice
: "";
334 if (strcasecmp(value
, constptr
->option
->defchoice
))
336 num_newopts
= cupsAddOption(constptr
->option
->keyword
,
337 constptr
->option
->defchoice
,
338 num_newopts
, &newopts
);
343 if (ignored
&& !changed
)
346 * No choice, have to back out this selection...
349 if ((value
= cupsGetOption(ignored
->keyword
, num_newopts
,
352 marked
= ppdFindMarkedChoice(ppd
, ignored
->keyword
);
353 value
= marked
? marked
->choice
: "";
356 if (strcasecmp(value
, ignored
->defchoice
))
358 num_newopts
= cupsAddOption(ignored
->keyword
, ignored
->defchoice
,
359 num_newopts
, &newopts
);
367 DEBUG_puts("ppdResolveConflicts: Unable to automatically resolve "
373 cupsArrayClear(pass
);
374 cupsArrayDelete(active
);
378 * Free the caller's option array...
381 cupsFreeOptions(*num_options
, *options
);
384 * If Collate is the option we are testing, add it here. Otherwise, remove
385 * any Collate option from the resolve list since the filters automatically
386 * handle manual collation...
389 if (option
&& !strcasecmp(option
, "Collate"))
390 num_newopts
= cupsAddOption(option
, choice
, num_newopts
, &newopts
);
392 num_newopts
= cupsRemoveOption("Collate", num_newopts
, &newopts
);
395 * Return the new list of options to the caller...
398 *num_options
= num_newopts
;
401 cupsArrayDelete(pass
);
402 cupsArrayDelete(resolvers
);
404 cupsArrayRestore(ppd
->sorted_attrs
);
409 * If we get here, we failed to resolve...
414 cupsFreeOptions(num_newopts
, newopts
);
416 cupsArrayDelete(pass
);
417 cupsArrayDelete(resolvers
);
419 cupsArrayRestore(ppd
->sorted_attrs
);
426 * 'ppd_is_installable()' - Determine whether an option is in the
427 * InstallableOptions group.
430 static int /* O - 1 if installable, 0 if normal */
432 ppd_group_t
*installable
, /* I - InstallableOptions group */
433 const char *name
) /* I - Option name */
437 int i
; /* Looping var */
438 ppd_option_t
*option
; /* Current option */
441 for (i
= installable
->num_options
, option
= installable
->options
;
444 if (!strcasecmp(option
->keyword
, name
))
453 * 'ppd_load_constraints()' - Load constraints from a PPD file.
457 ppd_load_constraints(ppd_file_t
*ppd
) /* I - PPD file */
459 int i
; /* Looping var */
460 ppd_const_t
*oldconst
; /* Current UIConstraints data */
461 ppd_attr_t
*constattr
; /* Current cupsUIConstraints attribute */
462 _ppd_cups_uiconsts_t
*consts
; /* Current cupsUIConstraints data */
463 _ppd_cups_uiconst_t
*constptr
; /* Current constraint */
464 ppd_group_t
*installable
; /* Installable options group */
465 const char *vptr
; /* Pointer into constraint value */
466 char option
[PPD_MAX_NAME
], /* Option name/MainKeyword */
467 choice
[PPD_MAX_NAME
], /* Choice/OptionKeyword */
468 *ptr
; /* Pointer into option or choice */
472 * Create an array to hold the constraint data...
475 ppd
->cups_uiconstraints
= cupsArrayNew(NULL
, NULL
);
478 * Find the installable options group if it exists...
481 for (i
= ppd
->num_groups
, installable
= ppd
->groups
;
483 i
--, installable
++)
484 if (!strcasecmp(installable
->name
, "InstallableOptions"))
491 * See what kind of constraint data we have in the PPD...
494 if ((constattr
= ppdFindAttr(ppd
, "cupsUIConstraints", NULL
)) != NULL
)
497 * Load new-style cupsUIConstraints data...
501 constattr
= ppdFindNextAttr(ppd
, "cupsUIConstraints", NULL
))
503 if (!constattr
->value
)
505 DEBUG_puts("ppd_load_constraints: Bad cupsUIConstraints value!");
509 for (i
= 0, vptr
= strchr(constattr
->value
, '*');
511 i
++, vptr
= strchr(vptr
+ 1, '*'));
515 DEBUG_puts("ppd_load_constraints: Bad cupsUIConstraints value!");
519 if ((consts
= calloc(1, sizeof(_ppd_cups_uiconsts_t
))) == NULL
)
521 DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
522 "cupsUIConstraints!");
526 if ((constptr
= calloc(i
, sizeof(_ppd_cups_uiconst_t
))) == NULL
)
529 DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
530 "cupsUIConstraints!");
534 consts
->num_constraints
= i
;
535 consts
->constraints
= constptr
;
537 strlcpy(consts
->resolver
, constattr
->spec
, sizeof(consts
->resolver
));
539 for (i
= 0, vptr
= strchr(constattr
->value
, '*');
541 i
++, vptr
= strchr(vptr
, '*'), constptr
++)
544 * Extract "*Option Choice" or just "*Option"...
547 for (vptr
++, ptr
= option
; *vptr
&& !isspace(*vptr
& 255); vptr
++)
548 if (ptr
< (option
+ sizeof(option
) - 1))
553 while (isspace(*vptr
& 255))
560 for (ptr
= choice
; *vptr
&& !isspace(*vptr
& 255); vptr
++)
561 if (ptr
< (choice
+ sizeof(choice
) - 1))
567 if (!strncasecmp(option
, "Custom", 6) && !strcasecmp(choice
, "True"))
569 _cups_strcpy(option
, option
+ 6);
570 strcpy(choice
, "Custom");
573 constptr
->option
= ppdFindOption(ppd
, option
);
574 constptr
->choice
= ppdFindChoice(constptr
->option
, choice
);
575 constptr
->installable
= ppd_is_installable(installable
, option
);
576 consts
->installable
|= constptr
->installable
;
578 if (!constptr
->option
|| (!constptr
->choice
&& choice
[0]))
580 DEBUG_printf(("ppd_load_constraints: Unknown option *%s %s!\n",
587 cupsArrayAdd(ppd
->cups_uiconstraints
, consts
);
590 free(consts
->constraints
);
598 * Load old-style [Non]UIConstraints data...
601 for (i
= ppd
->num_consts
, oldconst
= ppd
->consts
; i
> 0; i
--, oldconst
++)
604 * Weed out nearby duplicates, since the PPD spec requires that you
605 * define both "*Foo foo *Bar bar" and "*Bar bar *Foo foo"...
609 !strcasecmp(oldconst
[0].option1
, oldconst
[1].option2
) &&
610 !strcasecmp(oldconst
[0].choice1
, oldconst
[1].choice2
) &&
611 !strcasecmp(oldconst
[0].option2
, oldconst
[1].option1
) &&
612 !strcasecmp(oldconst
[0].choice2
, oldconst
[1].choice1
))
619 if ((consts
= calloc(1, sizeof(_ppd_cups_uiconsts_t
))) == NULL
)
621 DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
626 if ((constptr
= calloc(2, sizeof(_ppd_cups_uiconst_t
))) == NULL
)
629 DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
635 * Fill in the information...
638 consts
->num_constraints
= 2;
639 consts
->constraints
= constptr
;
641 if (!strncasecmp(oldconst
->option1
, "Custom", 6) &&
642 !strcasecmp(oldconst
->choice1
, "True"))
644 constptr
[0].option
= ppdFindOption(ppd
, oldconst
->option1
+ 6);
645 constptr
[0].choice
= ppdFindChoice(constptr
[0].option
, "Custom");
646 constptr
[0].installable
= 0;
650 constptr
[0].option
= ppdFindOption(ppd
, oldconst
->option1
);
651 constptr
[0].choice
= ppdFindChoice(constptr
[0].option
,
653 constptr
[0].installable
= ppd_is_installable(installable
,
657 if (!constptr
[0].option
|| (!constptr
[0].choice
&& oldconst
->choice1
[0]))
659 DEBUG_printf(("ppd_load_constraints: Unknown option *%s %s!\n",
660 oldconst
->option1
, oldconst
->choice1
));
661 free(consts
->constraints
);
666 if (!strncasecmp(oldconst
->option2
, "Custom", 6) &&
667 !strcasecmp(oldconst
->choice2
, "True"))
669 constptr
[1].option
= ppdFindOption(ppd
, oldconst
->option2
+ 6);
670 constptr
[1].choice
= ppdFindChoice(constptr
[1].option
, "Custom");
671 constptr
[1].installable
= 0;
675 constptr
[1].option
= ppdFindOption(ppd
, oldconst
->option2
);
676 constptr
[1].choice
= ppdFindChoice(constptr
[1].option
,
678 constptr
[1].installable
= ppd_is_installable(installable
,
682 if (!constptr
[1].option
|| (!constptr
[1].choice
&& oldconst
->choice2
[0]))
684 DEBUG_printf(("ppd_load_constraints: Unknown option *%s %s!\n",
685 oldconst
->option2
, oldconst
->choice2
));
686 free(consts
->constraints
);
691 consts
->installable
= constptr
[0].installable
|| constptr
[1].installable
;
694 * Add it to the constraints array...
697 cupsArrayAdd(ppd
->cups_uiconstraints
, consts
);
705 * 'ppd_test_constraints()' - See if any constraints are active.
708 static cups_array_t
* /* O - Array of active constraints */
709 ppd_test_constraints(
710 ppd_file_t
*ppd
, /* I - PPD file */
711 int num_options
, /* I - Number of additional options */
712 cups_option_t
*options
, /* I - Additional options */
713 int which
) /* I - Which constraints to test */
715 int i
; /* Looping var */
716 _ppd_cups_uiconsts_t
*consts
; /* Current constraints */
717 _ppd_cups_uiconst_t
*constptr
; /* Current constraint */
718 ppd_choice_t key
, /* Search key */
719 *marked
; /* Marked choice */
720 cups_array_t
*active
= NULL
; /* Active constraints */
721 const char *value
; /* Current value */
724 if (!ppd
->cups_uiconstraints
)
725 ppd_load_constraints(ppd
);
727 cupsArraySave(ppd
->marked
);
729 for (consts
= (_ppd_cups_uiconsts_t
*)cupsArrayFirst(ppd
->cups_uiconstraints
);
731 consts
= (_ppd_cups_uiconsts_t
*)cupsArrayNext(ppd
->cups_uiconstraints
))
733 if (which
!= _PPD_ALL_CONSTRAINTS
&& which
!= consts
->installable
)
736 for (i
= consts
->num_constraints
, constptr
= consts
->constraints
;
740 if (constptr
->choice
&&
741 (!strcasecmp(constptr
->option
->keyword
, "PageSize") ||
742 !strcasecmp(constptr
->option
->keyword
, "PageRegion")))
745 * PageSize and PageRegion are used depending on the selected input slot
746 * and manual feed mode. Validate against the selected page size instead
747 * of an individual option...
750 if ((value
= cupsGetOption("PageSize", num_options
, options
)) == NULL
)
751 if ((value
= cupsGetOption("PageRegion", num_options
,
753 if ((value
= cupsGetOption("media", num_options
, options
)) == NULL
)
755 ppd_size_t
*size
= ppdPageSize(ppd
, NULL
);
761 if (!value
|| strcasecmp(value
, constptr
->choice
->choice
))
764 else if (constptr
->choice
)
766 if ((value
= cupsGetOption(constptr
->option
->keyword
, num_options
,
769 if (strcasecmp(value
, constptr
->choice
->choice
))
772 else if (!constptr
->choice
->marked
)
775 else if ((value
= cupsGetOption(constptr
->option
->keyword
, num_options
,
778 if (!strcasecmp(value
, "None") || !strcasecmp(value
, "Off") ||
779 !strcasecmp(value
, "False"))
784 key
.option
= constptr
->option
;
786 if ((marked
= (ppd_choice_t
*)cupsArrayFind(ppd
->marked
, &key
))
788 (!strcasecmp(marked
->choice
, "None") ||
789 !strcasecmp(marked
->choice
, "Off") ||
790 !strcasecmp(marked
->choice
, "False")))
798 active
= cupsArrayNew(NULL
, NULL
);
800 cupsArrayAdd(active
, consts
);
804 cupsArrayRestore(ppd
->marked
);