/*
* "$Id$"
*
- * Option marking routines for the Common UNIX Printing System (CUPS).
+ * Option marking routines for CUPS.
*
- * Copyright 2007-2008 by Apple Inc.
+ * Copyright 2007-2012 by Apple Inc.
* Copyright 1997-2007 by Easy Software Products, all rights reserved.
*
* These coded instructions, statements, and computer programs are the
*
* Contents:
*
+ * cupsGetConflicts() - Get a list of conflicting options in a marked
+ * PPD.
+ * cupsResolveConflicts() - Resolve conflicts in a marked PPD.
* ppdConflicts() - Check to see if there are any conflicts among
* the marked option choices.
* ppdInstallableConflict() - Test whether an option choice conflicts with an
* installable option.
- * cupsResolveConflicts() - Resolve conflicts in a marked PPD.
* ppd_is_installable() - Determine whether an option is in the
* InstallableOptions group.
* ppd_load_constraints() - Load constraints from a PPD file.
* Include necessary headers...
*/
+#include "cups-private.h"
#include "ppd-private.h"
-#include "string.h"
-#include "debug.h"
/*
enum
{
_PPD_NORMAL_CONSTRAINTS,
+ _PPD_OPTION_CONSTRAINTS,
_PPD_INSTALLABLE_CONSTRAINTS,
_PPD_ALL_CONSTRAINTS
};
static int ppd_is_installable(ppd_group_t *installable,
const char *option);
static void ppd_load_constraints(ppd_file_t *ppd);
-static cups_array_t *ppd_test_constraints(ppd_file_t *ppd, int num_options,
+static cups_array_t *ppd_test_constraints(ppd_file_t *ppd,
+ const char *option,
+ const char *choice,
+ int num_options,
cups_option_t *options,
int which);
/*
- * 'ppdConflicts()' - Check to see if there are any conflicts among the
- * marked option choices.
+ * 'cupsGetConflicts()' - Get a list of conflicting options in a marked PPD.
*
- * The returned value is the same as returned by @link ppdMarkOption@.
+ * This function gets a list of options that would conflict if "option" and
+ * "choice" were marked in the PPD. You would typically call this function
+ * after marking the currently selected options in the PPD in order to
+ * determine whether a new option selection would cause a conflict.
+ *
+ * The number of conflicting options are returned with "options" pointing to
+ * the conflicting options. The returned option array must be freed using
+ * @link cupsFreeOptions@.
+ *
+ * @since CUPS 1.4/OS X 10.6@
*/
-int /* O - Number of conflicts found */
-ppdConflicts(ppd_file_t *ppd) /* I - PPD to check */
+int /* O - Number of conflicting options */
+cupsGetConflicts(
+ ppd_file_t *ppd, /* I - PPD file */
+ const char *option, /* I - Option to test */
+ const char *choice, /* I - Choice to test */
+ cups_option_t **options) /* O - Conflicting options */
{
- int i, /* Looping variable */
- conflicts; /* Number of conflicts */
+ int i, /* Looping var */
+ num_options; /* Number of conflicting options */
cups_array_t *active; /* Active conflicts */
_ppd_cups_uiconsts_t *c; /* Current constraints */
_ppd_cups_uiconst_t *cptr; /* Current constraint */
- ppd_option_t *o; /* Current option */
+ ppd_choice_t *marked; /* Marked choice */
- if (!ppd)
- return (0);
-
/*
- * Clear all conflicts...
+ * Range check input...
*/
- for (o = ppdFirstOption(ppd); o; o = ppdNextOption(ppd))
- o->conflicted = 0;
+ if (options)
+ *options = NULL;
+
+ if (!ppd || !option || !choice || !options)
+ return (0);
/*
* Test for conflicts...
*/
- active = ppd_test_constraints(ppd, 0, NULL, _PPD_ALL_CONSTRAINTS);
- conflicts = cupsArrayCount(active);
+ active = ppd_test_constraints(ppd, option, choice, 0, NULL,
+ _PPD_ALL_CONSTRAINTS);
/*
- * Loop through all of the UI constraints and flag any options
- * that conflict...
+ * Loop through all of the UI constraints and add any options that conflict...
*/
- for (c = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active);
+ for (num_options = 0, c = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active);
c;
c = (_ppd_cups_uiconsts_t *)cupsArrayNext(active))
{
for (i = c->num_constraints, cptr = c->constraints;
i > 0;
i --, cptr ++)
- cptr->option->conflicted = 1;
+ if (_cups_strcasecmp(cptr->option->keyword, option))
+ {
+ if (cptr->choice)
+ num_options = cupsAddOption(cptr->option->keyword,
+ cptr->choice->choice, num_options,
+ options);
+ else if ((marked = ppdFindMarkedChoice(ppd,
+ cptr->option->keyword)) != NULL)
+ num_options = cupsAddOption(cptr->option->keyword, marked->choice,
+ num_options, options);
+ }
}
cupsArrayDelete(active);
- /*
- * Return the number of conflicts found...
- */
-
- return (conflicts);
-}
-
-
-/*
- * 'ppdInstallableConflict()' - Test whether an option choice conflicts with
- * an installable option.
- *
- * This function tests whether a particular option choice is available based
- * on constraints against options in the "InstallableOptions" group.
- *
- * @since CUPS 1.4@
- */
-
-int /* O - 1 if conflicting, 0 if not conflicting */
-ppdInstallableConflict(
- ppd_file_t *ppd, /* I - PPD file */
- const char *option, /* I - Option */
- const char *choice) /* I - Choice */
-{
- cups_array_t *active; /* Active conflicts */
- cups_option_t test; /* Test against this option */
-
-
- /*
- * Range check input...
- */
-
- if (!ppd || !option || !choice)
- return (0);
-
- /*
- * Test constraints using the new option...
- */
-
- test.name = (char *)option;
- test.value = (char *)choice;
- active = ppd_test_constraints(ppd, 1, &test,
- _PPD_INSTALLABLE_CONSTRAINTS);
-
- cupsArrayDelete(active);
-
- return (active != NULL);
+ return (num_options);
}
* 'cupsResolveConflicts()' - Resolve conflicts in a marked PPD.
*
* This function attempts to resolve any conflicts in a marked PPD, returning
- * a list of option changes that are required to resolve any conflicts. On
- * input, "num_options" and "options" contain any pending option changes that
- * have not yet been marked, while "option" and "choice" contain the most recent
+ * a list of option changes that are required to resolve them. On input,
+ * "num_options" and "options" contain any pending option changes that have
+ * not yet been marked, while "option" and "choice" contain the most recent
* selection which may or may not be in "num_options" or "options".
*
* On successful return, "num_options" and "options" are updated to contain
* "option" and "choice" along with any changes required to resolve conflicts
- * specified in the PPD file. If option conflicts cannot be resolved,
- * "num_options" and "options" are not changed.
+ * specified in the PPD file and 1 is returned.
+ *
+ * If option conflicts cannot be resolved, "num_options" and "options" are not
+ * changed and 0 is returned.
*
- * @code ppdResolveConflicts@ uses one of two sources of option constraint
- * information. The preferred constraint information is defined by
+ * When resolving conflicts, @code cupsResolveConflicts@ does not consider
+ * changes to the current page size (@code media@, @code PageSize@, and
+ * @code PageRegion@) or to the most recent option specified in "option".
+ * Thus, if the only way to resolve a conflict is to change the page size
+ * or the option the user most recently changed, @code cupsResolveConflicts@
+ * will return 0 to indicate it was unable to resolve the conflicts.
+ *
+ * The @code cupsResolveConflicts@ function uses one of two sources of option
+ * constraint information. The preferred constraint information is defined by
* @code cupsUIConstraints@ and @code cupsUIResolver@ attributes - in this
- * case, the PPD file provides constraint resolution actions. In this case,
- * it should not be possible for @ppdResolveConflicts@ to fail, however it
- * will do so if a resolver loop is detected.
+ * case, the PPD file provides constraint resolution actions.
*
- * The backup constraint infomration is defined by the
+ * The backup constraint information is defined by the
* @code UIConstraints@ and @code NonUIConstraints@ attributes. These
- * constraints are resolved algorithmically by selecting the default choice
- * for the conflicting option. Unfortunately, this method is far more likely
- * to fail.
+ * constraints are resolved algorithmically by first selecting the default
+ * choice for the conflicting option, then iterating over all possible choices
+ * until a non-conflicting option choice is found.
*
- * @since CUPS 1.4@
+ * @since CUPS 1.4/OS X 10.6@
*/
int /* O - 1 on success, 0 on failure */
cups_option_t **options) /* IO - Additional selected options */
{
int i, /* Looping var */
+ tries, /* Number of tries */
num_newopts; /* Number of new options */
cups_option_t *newopts; /* New options */
cups_array_t *active, /* Active constraints */
*pass, /* Resolvers for this pass */
- *resolvers; /* Resolvers we have used */
+ *resolvers, /* Resolvers we have used */
+ *test; /* Test array for conflicts */
_ppd_cups_uiconsts_t *consts; /* Current constraints */
_ppd_cups_uiconst_t *constptr; /* Current constraint */
ppd_attr_t *resolver; /* Current resolver */
+ const char *resval; /* Pointer into resolver value */
+ char resoption[PPD_MAX_NAME],
+ /* Current resolver option */
+ reschoice[PPD_MAX_NAME],
+ /* Current resolver choice */
+ *resptr, /* Pointer into option/choice */
+ firstpage[255]; /* AP_FIRSTPAGE_Keyword string */
const char *value; /* Selected option value */
int changed; /* Did we change anything? */
ppd_choice_t *marked; /* Marked choice */
- ppd_option_t *ignored; /* Ignored option */
/*
for (i = 0; i < *num_options; i ++)
num_newopts = cupsAddOption((*options)[i].name, (*options)[i].value,
num_newopts, &newopts);
- if (option)
+ if (option && _cups_strcasecmp(option, "Collate"))
num_newopts = cupsAddOption(option, choice, num_newopts, &newopts);
/*
cupsArraySave(ppd->sorted_attrs);
resolvers = NULL;
- pass = cupsArrayNew((cups_array_func_t)strcasecmp, NULL);
+ pass = cupsArrayNew((cups_array_func_t)_cups_strcasecmp, NULL);
+ tries = 0;
- while ((active = ppd_test_constraints(ppd, num_newopts, newopts,
+ while (tries < 100 &&
+ (active = ppd_test_constraints(ppd, NULL, NULL, num_newopts, newopts,
_PPD_ALL_CONSTRAINTS)) != NULL)
{
+ tries ++;
+
if (!resolvers)
- resolvers = cupsArrayNew((cups_array_func_t)strcasecmp, NULL);
+ resolvers = cupsArrayNew((cups_array_func_t)_cups_strcasecmp, NULL);
for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active), changed = 0;
consts;
* Resolver loop!
*/
- DEBUG_printf(("ppdResolveConflicts: Resolver loop with %s!\n",
+ DEBUG_printf(("1cupsResolveConflicts: Resolver loop with %s!",
consts->resolver));
goto error;
}
if ((resolver = ppdFindAttr(ppd, "cupsUIResolver",
consts->resolver)) == NULL)
{
- DEBUG_printf(("ppdResolveConflicts: Resolver %s not found!\n",
+ DEBUG_printf(("1cupsResolveConflicts: Resolver %s not found!",
consts->resolver));
goto error;
}
if (!resolver->value)
{
- DEBUG_printf(("ppdResolveConflicts: Resolver %s has no value!\n",
+ DEBUG_printf(("1cupsResolveConflicts: Resolver %s has no value!",
consts->resolver));
goto error;
}
cupsArrayAdd(pass, consts->resolver);
cupsArrayAdd(resolvers, consts->resolver);
- num_newopts = _ppdParseOptions(resolver->value, num_newopts, &newopts);
- changed = 1;
+ for (resval = resolver->value; *resval && !changed;)
+ {
+ while (_cups_isspace(*resval))
+ resval ++;
+
+ if (*resval != '*')
+ break;
+
+ for (resval ++, resptr = resoption;
+ *resval && !_cups_isspace(*resval);
+ resval ++)
+ if (resptr < (resoption + sizeof(resoption) - 1))
+ *resptr++ = *resval;
+
+ *resptr = '\0';
+
+ while (_cups_isspace(*resval))
+ resval ++;
+
+ for (resptr = reschoice;
+ *resval && !_cups_isspace(*resval);
+ resval ++)
+ if (resptr < (reschoice + sizeof(reschoice) - 1))
+ *resptr++ = *resval;
+
+ *resptr = '\0';
+
+ if (!resoption[0] || !reschoice[0])
+ break;
+
+ /*
+ * Is this the option we are changing?
+ */
+
+ snprintf(firstpage, sizeof(firstpage), "AP_FIRSTPAGE_%s", resoption);
+
+ if (option &&
+ (!_cups_strcasecmp(resoption, option) ||
+ !_cups_strcasecmp(firstpage, option) ||
+ (!_cups_strcasecmp(option, "PageSize") &&
+ !_cups_strcasecmp(resoption, "PageRegion")) ||
+ (!_cups_strcasecmp(option, "AP_FIRSTPAGE_PageSize") &&
+ !_cups_strcasecmp(resoption, "PageSize")) ||
+ (!_cups_strcasecmp(option, "AP_FIRSTPAGE_PageSize") &&
+ !_cups_strcasecmp(resoption, "PageRegion")) ||
+ (!_cups_strcasecmp(option, "PageRegion") &&
+ !_cups_strcasecmp(resoption, "PageSize")) ||
+ (!_cups_strcasecmp(option, "AP_FIRSTPAGE_PageRegion") &&
+ !_cups_strcasecmp(resoption, "PageSize")) ||
+ (!_cups_strcasecmp(option, "AP_FIRSTPAGE_PageRegion") &&
+ !_cups_strcasecmp(resoption, "PageRegion"))))
+ continue;
+
+ /*
+ * Try this choice...
+ */
+
+ if ((test = ppd_test_constraints(ppd, resoption, reschoice,
+ num_newopts, newopts,
+ _PPD_ALL_CONSTRAINTS)) == NULL)
+ {
+ /*
+ * That worked...
+ */
+
+ changed = 1;
+ }
+ else
+ cupsArrayDelete(test);
+
+ /*
+ * Add the option/choice from the resolver regardless of whether it
+ * worked; this makes sure that we can cascade several changes to
+ * make things resolve...
+ */
+
+ num_newopts = cupsAddOption(resoption, reschoice, num_newopts,
+ &newopts);
+ }
}
else
{
/*
* Try resolving by choosing the default values for non-installable
- * options...
+ * options, then by iterating through the possible choices...
*/
- for (i = consts->num_constraints, constptr = consts->constraints,
- ignored = NULL;
- i > 0;
+ int j; /* Looping var */
+ ppd_choice_t *cptr; /* Current choice */
+ ppd_size_t *size; /* Current page size */
+
+
+ for (i = consts->num_constraints, constptr = consts->constraints;
+ i > 0 && !changed;
i --, constptr ++)
{
- if (constptr->installable ||
- !strcasecmp(constptr->option->keyword, "PageSize") ||
- !strcasecmp(constptr->option->keyword, "PageRegion"))
+ /*
+ * Can't resolve by changing an installable option...
+ */
+
+ if (constptr->installable)
continue;
- if (option && !strcasecmp(constptr->option->keyword, option))
- {
- ignored = constptr->option;
+ /*
+ * Is this the option we are changing?
+ */
+
+ if (option &&
+ (!_cups_strcasecmp(constptr->option->keyword, option) ||
+ (!_cups_strcasecmp(option, "PageSize") &&
+ !_cups_strcasecmp(constptr->option->keyword, "PageRegion")) ||
+ (!_cups_strcasecmp(option, "PageRegion") &&
+ !_cups_strcasecmp(constptr->option->keyword, "PageSize"))))
continue;
- }
+
+ /*
+ * Get the current option choice...
+ */
if ((value = cupsGetOption(constptr->option->keyword, num_newopts,
newopts)) == NULL)
{
- marked = ppdFindMarkedChoice(ppd, constptr->option->keyword);
- value = marked ? marked->choice : "";
+ if (!_cups_strcasecmp(constptr->option->keyword, "PageSize") ||
+ !_cups_strcasecmp(constptr->option->keyword, "PageRegion"))
+ {
+ if ((value = cupsGetOption("PageSize", num_newopts,
+ newopts)) == NULL)
+ value = cupsGetOption("PageRegion", num_newopts, newopts);
+
+ if (!value)
+ {
+ if ((size = ppdPageSize(ppd, NULL)) != NULL)
+ value = size->name;
+ else
+ value = "";
+ }
+ }
+ else
+ {
+ marked = ppdFindMarkedChoice(ppd, constptr->option->keyword);
+ value = marked ? marked->choice : "";
+ }
}
- if (strcasecmp(value, constptr->option->defchoice))
- {
- num_newopts = cupsAddOption(constptr->option->keyword,
- constptr->option->defchoice,
- num_newopts, &newopts);
- changed = 1;
- }
- }
+ if (!_cups_strncasecmp(value, "Custom.", 7))
+ value = "Custom";
- if (ignored && !changed)
- {
- /*
- * No choice, have to back out this selection...
+ /*
+ * Try the default choice...
*/
- if ((value = cupsGetOption(ignored->keyword, num_newopts,
- newopts)) == NULL)
- {
- marked = ppdFindMarkedChoice(ppd, ignored->keyword);
- value = marked ? marked->choice : "";
- }
+ test = NULL;
- if (strcasecmp(value, ignored->defchoice))
+ if (_cups_strcasecmp(value, constptr->option->defchoice) &&
+ (test = ppd_test_constraints(ppd, constptr->option->keyword,
+ constptr->option->defchoice,
+ num_newopts, newopts,
+ _PPD_OPTION_CONSTRAINTS)) == NULL)
{
- num_newopts = cupsAddOption(ignored->keyword, ignored->defchoice,
+ /*
+ * That worked...
+ */
+
+ num_newopts = cupsAddOption(constptr->option->keyword,
+ constptr->option->defchoice,
num_newopts, &newopts);
changed = 1;
}
- }
- }
+ else
+ {
+ /*
+ * Try each choice instead...
+ */
+
+ for (j = constptr->option->num_choices,
+ cptr = constptr->option->choices;
+ j > 0;
+ j --, cptr ++)
+ {
+ cupsArrayDelete(test);
+ test = NULL;
+
+ if (_cups_strcasecmp(value, cptr->choice) &&
+ _cups_strcasecmp(constptr->option->defchoice, cptr->choice) &&
+ _cups_strcasecmp("Custom", cptr->choice) &&
+ (test = ppd_test_constraints(ppd, constptr->option->keyword,
+ cptr->choice, num_newopts,
+ newopts,
+ _PPD_OPTION_CONSTRAINTS)) == NULL)
+ {
+ /*
+ * This choice works...
+ */
+
+ num_newopts = cupsAddOption(constptr->option->keyword,
+ cptr->choice, num_newopts,
+ &newopts);
+ changed = 1;
+ break;
+ }
+ }
- if (!changed)
- {
- DEBUG_puts("ppdResolveConflicts: Unable to automatically resolve "
- "constraint!");
- goto error;
+ cupsArrayDelete(test);
+ }
+ }
}
}
+ if (!changed)
+ {
+ DEBUG_puts("1cupsResolveConflicts: Unable to automatically resolve "
+ "constraint!");
+ goto error;
+ }
+
cupsArrayClear(pass);
cupsArrayDelete(active);
+ active = NULL;
}
+ if (tries >= 100)
+ goto error;
+
/*
- * Free either the old or the new options depending on whether we had to
- * apply any resolvers...
+ * Free the caller's option array...
*/
- if (resolvers)
- {
- cupsFreeOptions(*num_options, *options);
- *num_options = num_newopts;
- *options = newopts;
- }
+ cupsFreeOptions(*num_options, *options);
+
+ /*
+ * If Collate is the option we are testing, add it here. Otherwise, remove
+ * any Collate option from the resolve list since the filters automatically
+ * handle manual collation...
+ */
+
+ if (option && !_cups_strcasecmp(option, "Collate"))
+ num_newopts = cupsAddOption(option, choice, num_newopts, &newopts);
else
- cupsFreeOptions(num_newopts, newopts);
+ num_newopts = cupsRemoveOption("Collate", num_newopts, &newopts);
+
+ /*
+ * Return the new list of options to the caller...
+ */
+
+ *num_options = num_newopts;
+ *options = newopts;
cupsArrayDelete(pass);
cupsArrayDelete(resolvers);
cupsArrayRestore(ppd->sorted_attrs);
+ DEBUG_printf(("1cupsResolveConflicts: Returning %d options:", num_newopts));
+#ifdef DEBUG
+ for (i = 0; i < num_newopts; i ++)
+ DEBUG_printf(("1cupsResolveConflicts: options[%d]: %s=%s", i,
+ newopts[i].name, newopts[i].value));
+#endif /* DEBUG */
+
return (1);
/*
cupsFreeOptions(num_newopts, newopts);
+ cupsArrayDelete(active);
cupsArrayDelete(pass);
cupsArrayDelete(resolvers);
cupsArrayRestore(ppd->sorted_attrs);
+ DEBUG_puts("1cupsResolveConflicts: Unable to resolve conflicts!");
+
return (0);
}
+/*
+ * 'ppdConflicts()' - Check to see if there are any conflicts among the
+ * marked option choices.
+ *
+ * The returned value is the same as returned by @link ppdMarkOption@.
+ */
+
+int /* O - Number of conflicts found */
+ppdConflicts(ppd_file_t *ppd) /* I - PPD to check */
+{
+ int i, /* Looping variable */
+ conflicts; /* Number of conflicts */
+ cups_array_t *active; /* Active conflicts */
+ _ppd_cups_uiconsts_t *c; /* Current constraints */
+ _ppd_cups_uiconst_t *cptr; /* Current constraint */
+ ppd_option_t *o; /* Current option */
+
+
+ if (!ppd)
+ return (0);
+
+ /*
+ * Clear all conflicts...
+ */
+
+ cupsArraySave(ppd->options);
+
+ for (o = ppdFirstOption(ppd); o; o = ppdNextOption(ppd))
+ o->conflicted = 0;
+
+ cupsArrayRestore(ppd->options);
+
+ /*
+ * Test for conflicts...
+ */
+
+ active = ppd_test_constraints(ppd, NULL, NULL, 0, NULL,
+ _PPD_ALL_CONSTRAINTS);
+ conflicts = cupsArrayCount(active);
+
+ /*
+ * Loop through all of the UI constraints and flag any options
+ * that conflict...
+ */
+
+ for (c = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active);
+ c;
+ c = (_ppd_cups_uiconsts_t *)cupsArrayNext(active))
+ {
+ for (i = c->num_constraints, cptr = c->constraints;
+ i > 0;
+ i --, cptr ++)
+ cptr->option->conflicted = 1;
+ }
+
+ cupsArrayDelete(active);
+
+ /*
+ * Return the number of conflicts found...
+ */
+
+ return (conflicts);
+}
+
+
+/*
+ * 'ppdInstallableConflict()' - Test whether an option choice conflicts with
+ * an installable option.
+ *
+ * This function tests whether a particular option choice is available based
+ * on constraints against options in the "InstallableOptions" group.
+ *
+ * @since CUPS 1.4/OS X 10.6@
+ */
+
+int /* O - 1 if conflicting, 0 if not conflicting */
+ppdInstallableConflict(
+ ppd_file_t *ppd, /* I - PPD file */
+ const char *option, /* I - Option */
+ const char *choice) /* I - Choice */
+{
+ cups_array_t *active; /* Active conflicts */
+
+
+ DEBUG_printf(("2ppdInstallableConflict(ppd=%p, option=\"%s\", choice=\"%s\")",
+ ppd, option, choice));
+
+ /*
+ * Range check input...
+ */
+
+ if (!ppd || !option || !choice)
+ return (0);
+
+ /*
+ * Test constraints using the new option...
+ */
+
+ active = ppd_test_constraints(ppd, option, choice, 0, NULL,
+ _PPD_INSTALLABLE_CONSTRAINTS);
+
+ cupsArrayDelete(active);
+
+ return (active != NULL);
+}
+
+
/*
* 'ppd_is_installable()' - Determine whether an option is in the
* InstallableOptions group.
for (i = installable->num_options, option = installable->options;
i > 0;
i --, option ++)
- if (!strcasecmp(option->keyword, name))
+ if (!_cups_strcasecmp(option->keyword, name))
return (1);
}
*ptr; /* Pointer into option or choice */
+ DEBUG_printf(("7ppd_load_constraints(ppd=%p)", ppd));
+
/*
* Create an array to hold the constraint data...
*/
for (i = ppd->num_groups, installable = ppd->groups;
i > 0;
i --, installable ++)
- if (!strcasecmp(installable->name, "InstallableOptions"))
+ if (!_cups_strcasecmp(installable->name, "InstallableOptions"))
break;
if (i <= 0)
installable = NULL;
/*
- * See what kind of constraint data we have in the PPD...
+ * Load old-style [Non]UIConstraints data...
*/
- if ((constattr = ppdFindAttr(ppd, "cupsUIConstraints", NULL)) != NULL)
+ for (i = ppd->num_consts, oldconst = ppd->consts; i > 0; i --, oldconst ++)
{
/*
- * Load new-style cupsUIConstraints data...
+ * Weed out nearby duplicates, since the PPD spec requires that you
+ * define both "*Foo foo *Bar bar" and "*Bar bar *Foo foo"...
*/
- for (; constattr;
- constattr = ppdFindNextAttr(ppd, "cupsUIConstraints", NULL))
- {
- if (!constattr->value)
- {
- DEBUG_puts("ppd_load_constraints: Bad cupsUIConstraints value!");
- continue;
- }
+ if (i > 1 &&
+ !_cups_strcasecmp(oldconst[0].option1, oldconst[1].option2) &&
+ !_cups_strcasecmp(oldconst[0].choice1, oldconst[1].choice2) &&
+ !_cups_strcasecmp(oldconst[0].option2, oldconst[1].option1) &&
+ !_cups_strcasecmp(oldconst[0].choice2, oldconst[1].choice1))
+ continue;
- for (i = 0, vptr = strchr(constattr->value, '*');
- vptr;
- i ++, vptr = strchr(vptr + 1, '*'));
+ /*
+ * Allocate memory...
+ */
- if (i == 0)
- {
- DEBUG_puts("ppd_load_constraints: Bad cupsUIConstraints value!");
- continue;
- }
+ if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL)
+ {
+ DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
+ "UIConstraints!");
+ return;
+ }
- if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL)
- {
- DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
- "cupsUIConstraints!");
- return;
- }
+ if ((constptr = calloc(2, sizeof(_ppd_cups_uiconst_t))) == NULL)
+ {
+ free(consts);
+ DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
+ "UIConstraints!");
+ return;
+ }
- if ((constptr = calloc(i, sizeof(_ppd_cups_uiconst_t))) == NULL)
- {
- free(consts);
- DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
- "cupsUIConstraints!");
- return;
- }
+ /*
+ * Fill in the information...
+ */
- consts->num_constraints = i;
- consts->constraints = constptr;
+ consts->num_constraints = 2;
+ consts->constraints = constptr;
- strlcpy(consts->resolver, constattr->spec, sizeof(consts->resolver));
+ if (!_cups_strncasecmp(oldconst->option1, "Custom", 6) &&
+ !_cups_strcasecmp(oldconst->choice1, "True"))
+ {
+ constptr[0].option = ppdFindOption(ppd, oldconst->option1 + 6);
+ constptr[0].choice = ppdFindChoice(constptr[0].option, "Custom");
+ constptr[0].installable = 0;
+ }
+ else
+ {
+ constptr[0].option = ppdFindOption(ppd, oldconst->option1);
+ constptr[0].choice = ppdFindChoice(constptr[0].option,
+ oldconst->choice1);
+ constptr[0].installable = ppd_is_installable(installable,
+ oldconst->option1);
+ }
- for (i = 0, vptr = strchr(constattr->value, '*');
- vptr;
- i ++, vptr = strchr(vptr + 1, '*'), constptr ++)
- {
- /*
- * Extract "*Option Choice" or just "*Option"...
- */
+ if (!constptr[0].option || (!constptr[0].choice && oldconst->choice1[0]))
+ {
+ DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!",
+ oldconst->option1, oldconst->choice1));
+ free(consts->constraints);
+ free(consts);
+ continue;
+ }
- for (vptr ++, ptr = option; *vptr && !isspace(*vptr & 255); vptr ++)
- if (ptr < (option + sizeof(option) - 1))
- *ptr++ = *vptr;
+ if (!_cups_strncasecmp(oldconst->option2, "Custom", 6) &&
+ !_cups_strcasecmp(oldconst->choice2, "True"))
+ {
+ constptr[1].option = ppdFindOption(ppd, oldconst->option2 + 6);
+ constptr[1].choice = ppdFindChoice(constptr[1].option, "Custom");
+ constptr[1].installable = 0;
+ }
+ else
+ {
+ constptr[1].option = ppdFindOption(ppd, oldconst->option2);
+ constptr[1].choice = ppdFindChoice(constptr[1].option,
+ oldconst->choice2);
+ constptr[1].installable = ppd_is_installable(installable,
+ oldconst->option2);
+ }
- *ptr = '\0';
+ if (!constptr[1].option || (!constptr[1].choice && oldconst->choice2[0]))
+ {
+ DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!",
+ oldconst->option2, oldconst->choice2));
+ free(consts->constraints);
+ free(consts);
+ continue;
+ }
- while (isspace(*vptr & 255))
- vptr ++;
+ consts->installable = constptr[0].installable || constptr[1].installable;
- if (*vptr == '*')
- {
- vptr --;
- choice[0] = '\0';
- }
- else
- {
- for (ptr = choice; *vptr && !isspace(*vptr & 255); vptr ++)
- if (ptr < (choice + sizeof(choice) - 1))
- *ptr++ = *vptr;
+ /*
+ * Add it to the constraints array...
+ */
- *ptr = '\0';
- }
+ cupsArrayAdd(ppd->cups_uiconstraints, consts);
+ }
- if (!strncasecmp(option, "Custom", 6) && !strcasecmp(choice, "True"))
- {
- _cups_strcpy(option, option + 6);
- strcpy(choice, "Custom");
- }
+ /*
+ * Then load new-style constraints...
+ */
- constptr->option = ppdFindOption(ppd, option);
- constptr->choice = ppdFindChoice(constptr->option, choice);
- constptr->installable = ppd_is_installable(installable, option);
- consts->installable |= constptr->installable;
+ for (constattr = ppdFindAttr(ppd, "cupsUIConstraints", NULL);
+ constattr;
+ constattr = ppdFindNextAttr(ppd, "cupsUIConstraints", NULL))
+ {
+ if (!constattr->value)
+ {
+ DEBUG_puts("8ppd_load_constraints: Bad cupsUIConstraints value!");
+ continue;
+ }
- if (!constptr->option)
- {
- DEBUG_printf(("ppd_load_constraints: Unknown option %s!\n", option));
- break;
- }
- }
+ for (i = 0, vptr = strchr(constattr->value, '*');
+ vptr;
+ i ++, vptr = strchr(vptr + 1, '*'));
- if (!vptr)
- cupsArrayAdd(ppd->cups_uiconstraints, consts);
- else
- {
- free(consts->constraints);
- free(consts);
- }
+ if (i == 0)
+ {
+ DEBUG_puts("8ppd_load_constraints: Bad cupsUIConstraints value!");
+ continue;
}
- }
- else
- {
- /*
- * Load old-style [Non]UIConstraints data...
- */
- for (i = ppd->num_consts, oldconst = ppd->consts; i > 0; i --, oldconst ++)
+ if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL)
{
- /*
- * Weed out nearby duplicates, since the PPD spec requires that you
- * define both "*Foo foo *Bar bar" and "*Bar bar *Foo foo"...
- */
-
- if (i > 1 &&
- !strcasecmp(oldconst[0].option1, oldconst[1].option2) &&
- !strcasecmp(oldconst[0].choice1, oldconst[1].choice2) &&
- !strcasecmp(oldconst[0].option2, oldconst[1].option1) &&
- !strcasecmp(oldconst[0].choice2, oldconst[1].choice1))
- continue;
+ DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
+ "cupsUIConstraints!");
+ return;
+ }
- /*
- * Allocate memory...
- */
+ if ((constptr = calloc(i, sizeof(_ppd_cups_uiconst_t))) == NULL)
+ {
+ free(consts);
+ DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
+ "cupsUIConstraints!");
+ return;
+ }
- if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL)
- {
- DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
- "UIConstraints!");
- return;
- }
+ consts->num_constraints = i;
+ consts->constraints = constptr;
- if ((constptr = calloc(2, sizeof(_ppd_cups_uiconst_t))) == NULL)
- {
- free(consts);
- DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
- "UIConstraints!");
- return;
- }
+ strlcpy(consts->resolver, constattr->spec, sizeof(consts->resolver));
+ for (i = 0, vptr = strchr(constattr->value, '*');
+ vptr;
+ i ++, vptr = strchr(vptr, '*'), constptr ++)
+ {
/*
- * Fill in the information...
+ * Extract "*Option Choice" or just "*Option"...
*/
- consts->num_constraints = 2;
- consts->constraints = constptr;
+ for (vptr ++, ptr = option; *vptr && !_cups_isspace(*vptr); vptr ++)
+ if (ptr < (option + sizeof(option) - 1))
+ *ptr++ = *vptr;
- if (!strncasecmp(oldconst->option1, "Custom", 6) &&
- !strcasecmp(oldconst->choice1, "True"))
- {
- constptr[0].option = ppdFindOption(ppd, oldconst->option1 + 6);
- constptr[0].choice = ppdFindChoice(constptr[0].option, "Custom");
- constptr[0].installable = 0;
- }
- else
- {
- constptr[0].option = ppdFindOption(ppd, oldconst->option1);
- constptr[0].choice = ppdFindChoice(constptr[0].option,
- oldconst->choice1);
- constptr[0].installable = ppd_is_installable(installable,
- oldconst->option1);
- }
+ *ptr = '\0';
- if (!constptr[0].option)
- {
- DEBUG_printf(("ppd_load_constraints: Unknown option %s!\n",
- oldconst->option1));
- free(consts->constraints);
- free(consts);
- continue;
- }
+ while (_cups_isspace(*vptr))
+ vptr ++;
- if (!strncasecmp(oldconst->option2, "Custom", 6) &&
- !strcasecmp(oldconst->choice2, "True"))
- {
- constptr[1].option = ppdFindOption(ppd, oldconst->option2 + 6);
- constptr[1].choice = ppdFindChoice(constptr[1].option, "Custom");
- constptr[1].installable = 0;
- }
+ if (*vptr == '*')
+ choice[0] = '\0';
else
{
- constptr[1].option = ppdFindOption(ppd, oldconst->option2);
- constptr[1].choice = ppdFindChoice(constptr[1].option,
- oldconst->choice2);
- constptr[1].installable = ppd_is_installable(installable,
- oldconst->option2);
+ for (ptr = choice; *vptr && !_cups_isspace(*vptr); vptr ++)
+ if (ptr < (choice + sizeof(choice) - 1))
+ *ptr++ = *vptr;
+
+ *ptr = '\0';
}
- if (!constptr->option)
+ if (!_cups_strncasecmp(option, "Custom", 6) && !_cups_strcasecmp(choice, "True"))
{
- DEBUG_printf(("ppd_load_constraints: Unknown option %s!\n",
- oldconst->option2));
- free(consts->constraints);
- free(consts);
- continue;
+ _cups_strcpy(option, option + 6);
+ strlcpy(choice, "Custom", sizeof(choice));
}
- consts->installable = constptr[0].installable || constptr[1].installable;
+ constptr->option = ppdFindOption(ppd, option);
+ constptr->choice = ppdFindChoice(constptr->option, choice);
+ constptr->installable = ppd_is_installable(installable, option);
+ consts->installable |= constptr->installable;
- /*
- * Add it to the constraints array...
- */
+ if (!constptr->option || (!constptr->choice && choice[0]))
+ {
+ DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!",
+ option, choice));
+ break;
+ }
+ }
+ if (!vptr)
cupsArrayAdd(ppd->cups_uiconstraints, consts);
+ else
+ {
+ free(consts->constraints);
+ free(consts);
}
}
}
-
/*
* 'ppd_test_constraints()' - See if any constraints are active.
*/
static cups_array_t * /* O - Array of active constraints */
ppd_test_constraints(
ppd_file_t *ppd, /* I - PPD file */
+ const char *option, /* I - Current option */
+ const char *choice, /* I - Current choice */
int num_options, /* I - Number of additional options */
cups_option_t *options, /* I - Additional options */
int which) /* I - Which constraints to test */
ppd_choice_t key, /* Search key */
*marked; /* Marked choice */
cups_array_t *active = NULL; /* Active constraints */
- const char *value; /* Current value */
+ const char *value, /* Current value */
+ *firstvalue; /* AP_FIRSTPAGE_Keyword value */
+ char firstpage[255]; /* AP_FIRSTPAGE_Keyword string */
+
+ DEBUG_printf(("7ppd_test_constraints(ppd=%p, option=\"%s\", choice=\"%s\", "
+ "num_options=%d, options=%p, which=%d)", ppd, option, choice,
+ num_options, options, which));
if (!ppd->cups_uiconstraints)
ppd_load_constraints(ppd);
+ DEBUG_printf(("9ppd_test_constraints: %d constraints!",
+ cupsArrayCount(ppd->cups_uiconstraints)));
+
cupsArraySave(ppd->marked);
for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(ppd->cups_uiconstraints);
consts;
consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(ppd->cups_uiconstraints))
{
- if (which != _PPD_ALL_CONSTRAINTS && which != consts->installable)
- continue;
+ DEBUG_printf(("9ppd_test_constraints: installable=%d, resolver=\"%s\", "
+ "num_constraints=%d option1=\"%s\", choice1=\"%s\", "
+ "option2=\"%s\", choice2=\"%s\", ...",
+ consts->installable, consts->resolver, consts->num_constraints,
+ consts->constraints[0].option->keyword,
+ consts->constraints[0].choice ?
+ consts->constraints[0].choice->choice : "",
+ consts->constraints[1].option->keyword,
+ consts->constraints[1].choice ?
+ consts->constraints[1].choice->choice : ""));
+
+ if (consts->installable && which < _PPD_INSTALLABLE_CONSTRAINTS)
+ continue; /* Skip installable option constraint */
+
+ if (!consts->installable && which == _PPD_INSTALLABLE_CONSTRAINTS)
+ continue; /* Skip non-installable option constraint */
+
+ if (which == _PPD_OPTION_CONSTRAINTS && option)
+ {
+ /*
+ * Skip constraints that do not involve the current option...
+ */
+
+ for (i = consts->num_constraints, constptr = consts->constraints;
+ i > 0;
+ i --, constptr ++)
+ {
+ if (!_cups_strcasecmp(constptr->option->keyword, option))
+ break;
+
+ if (!_cups_strncasecmp(option, "AP_FIRSTPAGE_", 13) &&
+ !_cups_strcasecmp(constptr->option->keyword, option + 13))
+ break;
+ }
+
+ if (!i)
+ continue;
+ }
+
+ DEBUG_puts("9ppd_test_constraints: Testing...");
for (i = consts->num_constraints, constptr = consts->constraints;
i > 0;
i --, constptr ++)
{
+ DEBUG_printf(("9ppd_test_constraints: %s=%s?", constptr->option->keyword,
+ constptr->choice ? constptr->choice->choice : ""));
+
if (constptr->choice &&
- (!strcasecmp(constptr->option->keyword, "PageSize") ||
- !strcasecmp(constptr->option->keyword, "PageRegion")))
+ (!_cups_strcasecmp(constptr->option->keyword, "PageSize") ||
+ !_cups_strcasecmp(constptr->option->keyword, "PageRegion")))
{
/*
* PageSize and PageRegion are used depending on the selected input slot
* of an individual option...
*/
- if ((value = cupsGetOption("PageSize", num_options, options)) == NULL)
+ if (option && choice &&
+ (!_cups_strcasecmp(option, "PageSize") ||
+ !_cups_strcasecmp(option, "PageRegion")))
+ {
+ value = choice;
+ }
+ else if ((value = cupsGetOption("PageSize", num_options,
+ options)) == NULL)
if ((value = cupsGetOption("PageRegion", num_options,
options)) == NULL)
if ((value = cupsGetOption("media", num_options, options)) == NULL)
value = size->name;
}
- if (!value || strcasecmp(value, constptr->choice->choice))
+ if (value && !_cups_strncasecmp(value, "Custom.", 7))
+ value = "Custom";
+
+ if (option && choice &&
+ (!_cups_strcasecmp(option, "AP_FIRSTPAGE_PageSize") ||
+ !_cups_strcasecmp(option, "AP_FIRSTPAGE_PageRegion")))
+ {
+ firstvalue = choice;
+ }
+ else if ((firstvalue = cupsGetOption("AP_FIRSTPAGE_PageSize",
+ num_options, options)) == NULL)
+ firstvalue = cupsGetOption("AP_FIRSTPAGE_PageRegion", num_options,
+ options);
+
+ if (firstvalue && !_cups_strncasecmp(firstvalue, "Custom.", 7))
+ firstvalue = "Custom";
+
+ if ((!value || _cups_strcasecmp(value, constptr->choice->choice)) &&
+ (!firstvalue || _cups_strcasecmp(firstvalue, constptr->choice->choice)))
+ {
+ DEBUG_puts("9ppd_test_constraints: NO");
break;
+ }
}
else if (constptr->choice)
{
- if ((value = cupsGetOption(constptr->option->keyword, num_options,
- options)) != NULL)
+ /*
+ * Compare against the constrained choice...
+ */
+
+ if (option && choice && !_cups_strcasecmp(option, constptr->option->keyword))
+ {
+ if (!_cups_strncasecmp(choice, "Custom.", 7))
+ value = "Custom";
+ else
+ value = choice;
+ }
+ else if ((value = cupsGetOption(constptr->option->keyword, num_options,
+ options)) != NULL)
{
- if (strcasecmp(value, constptr->choice->choice))
- break;
+ if (!_cups_strncasecmp(value, "Custom.", 7))
+ value = "Custom";
+ }
+ else if (constptr->choice->marked)
+ value = constptr->choice->choice;
+ else
+ value = NULL;
+
+ /*
+ * Now check AP_FIRSTPAGE_option...
+ */
+
+ snprintf(firstpage, sizeof(firstpage), "AP_FIRSTPAGE_%s",
+ constptr->option->keyword);
+
+ if (option && choice && !_cups_strcasecmp(option, firstpage))
+ {
+ if (!_cups_strncasecmp(choice, "Custom.", 7))
+ firstvalue = "Custom";
+ else
+ firstvalue = choice;
+ }
+ else if ((firstvalue = cupsGetOption(firstpage, num_options,
+ options)) != NULL)
+ {
+ if (!_cups_strncasecmp(firstvalue, "Custom.", 7))
+ firstvalue = "Custom";
+ }
+ else
+ firstvalue = NULL;
+
+ DEBUG_printf(("9ppd_test_constraints: value=%s, firstvalue=%s", value,
+ firstvalue));
+
+ if ((!value || _cups_strcasecmp(value, constptr->choice->choice)) &&
+ (!firstvalue || _cups_strcasecmp(firstvalue, constptr->choice->choice)))
+ {
+ DEBUG_puts("9ppd_test_constraints: NO");
+ break;
+ }
+ }
+ else if (option && choice &&
+ !_cups_strcasecmp(option, constptr->option->keyword))
+ {
+ if (!_cups_strcasecmp(choice, "None") || !_cups_strcasecmp(choice, "Off") ||
+ !_cups_strcasecmp(choice, "False"))
+ {
+ DEBUG_puts("9ppd_test_constraints: NO");
+ break;
}
- else if (!constptr->choice->marked)
- break;
}
else if ((value = cupsGetOption(constptr->option->keyword, num_options,
- options)) != NULL)
+ options)) != NULL)
{
- if (!strcasecmp(value, "None") || !strcasecmp(value, "Off") ||
- !strcasecmp(value, "False"))
- break;
+ if (!_cups_strcasecmp(value, "None") || !_cups_strcasecmp(value, "Off") ||
+ !_cups_strcasecmp(value, "False"))
+ {
+ DEBUG_puts("9ppd_test_constraints: NO");
+ break;
+ }
}
else
{
- key.option = constptr->option;
+ key.option = constptr->option;
if ((marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key))
- != NULL &&
- (!strcasecmp(marked->choice, "None") ||
- !strcasecmp(marked->choice, "Off") ||
- !strcasecmp(marked->choice, "False")))
+ == NULL ||
+ (!_cups_strcasecmp(marked->choice, "None") ||
+ !_cups_strcasecmp(marked->choice, "Off") ||
+ !_cups_strcasecmp(marked->choice, "False")))
+ {
+ DEBUG_puts("9ppd_test_constraints: NO");
break;
+ }
}
}
active = cupsArrayNew(NULL, NULL);
cupsArrayAdd(active, consts);
+ DEBUG_puts("9ppd_test_constraints: Added...");
}
}
cupsArrayRestore(ppd->marked);
+ DEBUG_printf(("8ppd_test_constraints: Found %d active constraints!",
+ cupsArrayCount(active)));
+
return (active);
}