#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,
const char *option,
const char *choice,
int num_options,
cups_option_t *options,
int which);
int
cupsGetConflicts(
ppd_file_t *ppd,
const char *option,
const char *choice,
cups_option_t **options)
{
int i,
num_options;
cups_array_t *active;
_ppd_cups_uiconsts_t *c;
_ppd_cups_uiconst_t *cptr;
if (options)
*options = NULL;
if (!ppd || !option || !choice || !options)
return (0);
active = ppd_test_constraints(ppd, option, choice, 0, NULL,
_PPD_ALL_CONSTRAINTS);
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 ++)
if (strcasecmp(cptr->option->keyword, option))
num_options = cupsAddOption(cptr->option->keyword, cptr->choice->choice,
num_options, options);
}
cupsArrayDelete(active);
return (num_options);
}
int
cupsResolveConflicts(
ppd_file_t *ppd,
const char *option,
const char *choice,
int *num_options,
cups_option_t **options)
{
int i,
tries,
num_newopts;
cups_option_t *newopts;
cups_array_t *active,
*pass,
*resolvers,
*test;
_ppd_cups_uiconsts_t *consts;
_ppd_cups_uiconst_t *constptr;
ppd_attr_t *resolver;
const char *resval;
char resoption[PPD_MAX_NAME],
reschoice[PPD_MAX_NAME],
*resptr;
const char *value;
int changed;
ppd_choice_t *marked;
if (!ppd || !num_options || !options || (option == NULL) != (choice == NULL))
return (0);
num_newopts = 0;
newopts = NULL;
for (i = 0; i < *num_options; i ++)
num_newopts = cupsAddOption((*options)[i].name, (*options)[i].value,
num_newopts, &newopts);
if (option && 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);
tries = 0;
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);
for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active), changed = 0;
consts;
consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(active))
{
if (consts->resolver[0])
{
if (cupsArrayFind(pass, consts->resolver))
continue;
if (cupsArrayFind(resolvers, consts->resolver))
{
DEBUG_printf(("1ppdResolveConflicts: Resolver loop with %s!",
consts->resolver));
goto error;
}
if ((resolver = ppdFindAttr(ppd, "cupsUIResolver",
consts->resolver)) == NULL)
{
DEBUG_printf(("1ppdResolveConflicts: Resolver %s not found!",
consts->resolver));
goto error;
}
if (!resolver->value)
{
DEBUG_printf(("1ppdResolveConflicts: Resolver %s has no value!",
consts->resolver));
goto error;
}
cupsArrayAdd(pass, consts->resolver);
cupsArrayAdd(resolvers, consts->resolver);
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;
if (option &&
(!strcasecmp(resoption, option) ||
(!strcasecmp(option, "PageSize") &&
!strcasecmp(resoption, "PageRegion")) ||
(!strcasecmp(option, "PageRegion") &&
!strcasecmp(resoption, "PageSize"))))
continue;
if ((test = ppd_test_constraints(ppd, resoption, reschoice,
num_newopts, newopts,
_PPD_ALL_CONSTRAINTS)) == NULL)
{
changed = 1;
}
else
cupsArrayDelete(test);
num_newopts = cupsAddOption(resoption, reschoice, num_newopts,
&newopts);
}
}
else
{
int j;
ppd_choice_t *cptr;
ppd_size_t *size;
for (i = consts->num_constraints, constptr = consts->constraints;
i > 0 && !changed;
i --, constptr ++)
{
if (constptr->installable)
continue;
if (option &&
(!strcasecmp(constptr->option->keyword, option) ||
(!strcasecmp(option, "PageSize") &&
!strcasecmp(constptr->option->keyword, "PageRegion")) ||
(!strcasecmp(option, "PageRegion") &&
!strcasecmp(constptr->option->keyword, "PageSize"))))
continue;
if ((value = cupsGetOption(constptr->option->keyword, num_newopts,
newopts)) == NULL)
{
if (!strcasecmp(constptr->option->keyword, "PageSize") ||
!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 (!strncasecmp(value, "Custom.", 7))
value = "Custom";
test = NULL;
if (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(constptr->option->keyword,
constptr->option->defchoice,
num_newopts, &newopts);
changed = 1;
}
else
{
for (j = constptr->option->num_choices,
cptr = constptr->option->choices;
j > 0;
j --, cptr ++)
{
cupsArrayDelete(test);
test = NULL;
if (strcasecmp(value, cptr->choice) &&
strcasecmp(constptr->option->defchoice, cptr->choice) &&
strcasecmp("Custom", cptr->choice) &&
(test = ppd_test_constraints(ppd, constptr->option->keyword,
cptr->choice, num_newopts,
newopts,
_PPD_OPTION_CONSTRAINTS)) == NULL)
{
num_newopts = cupsAddOption(constptr->option->keyword,
cptr->choice, num_newopts,
&newopts);
changed = 1;
break;
}
}
cupsArrayDelete(test);
}
}
}
if (!changed)
{
DEBUG_puts("1ppdResolveConflicts: Unable to automatically resolve "
"constraint!");
goto error;
}
}
cupsArrayClear(pass);
cupsArrayDelete(active);
active = NULL;
}
if (tries >= 100)
goto error;
cupsFreeOptions(*num_options, *options);
if (option && !strcasecmp(option, "Collate"))
num_newopts = cupsAddOption(option, choice, num_newopts, &newopts);
else
num_newopts = cupsRemoveOption("Collate", num_newopts, &newopts);
*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
return (1);
error:
cupsFreeOptions(num_newopts, newopts);
cupsArrayDelete(active);
cupsArrayDelete(pass);
cupsArrayDelete(resolvers);
cupsArrayRestore(ppd->sorted_attrs);
DEBUG_puts("1cupsResolveConflicts: Unable to resolve conflicts!");
return (0);
}
int
ppdConflicts(ppd_file_t *ppd)
{
int i,
conflicts;
cups_array_t *active;
_ppd_cups_uiconsts_t *c;
_ppd_cups_uiconst_t *cptr;
ppd_option_t *o;
if (!ppd)
return (0);
cupsArraySave(ppd->options);
for (o = ppdFirstOption(ppd); o; o = ppdNextOption(ppd))
o->conflicted = 0;
cupsArrayRestore(ppd->options);
active = ppd_test_constraints(ppd, NULL, NULL, 0, NULL,
_PPD_ALL_CONSTRAINTS);
conflicts = cupsArrayCount(active);
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 (conflicts);
}
int
ppdInstallableConflict(
ppd_file_t *ppd,
const char *option,
const char *choice)
{
cups_array_t *active;
DEBUG_printf(("2ppdInstallableConflict(ppd=%p, option=\"%s\", choice=\"%s\")",
ppd, option, choice));
if (!ppd || !option || !choice)
return (0);
active = ppd_test_constraints(ppd, option, choice, 0, NULL,
_PPD_INSTALLABLE_CONSTRAINTS);
cupsArrayDelete(active);
return (active != NULL);
}
static int
ppd_is_installable(
ppd_group_t *installable,
const char *name)
{
if (installable)
{
int i;
ppd_option_t *option;
for (i = installable->num_options, option = installable->options;
i > 0;
i --, option ++)
if (!strcasecmp(option->keyword, name))
return (1);
}
return (0);
}
static void
ppd_load_constraints(ppd_file_t *ppd)
{
int i;
ppd_const_t *oldconst;
ppd_attr_t *constattr;
_ppd_cups_uiconsts_t *consts;
_ppd_cups_uiconst_t *constptr;
ppd_group_t *installable;
const char *vptr;
char option[PPD_MAX_NAME],
choice[PPD_MAX_NAME],
*ptr;
DEBUG_printf(("7ppd_load_constraints(ppd=%p)", ppd));
ppd->cups_uiconstraints = cupsArrayNew(NULL, NULL);
for (i = ppd->num_groups, installable = ppd->groups;
i > 0;
i --, installable ++)
if (!strcasecmp(installable->name, "InstallableOptions"))
break;
if (i <= 0)
installable = NULL;
for (i = ppd->num_consts, oldconst = ppd->consts; i > 0; i --, oldconst ++)
{
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;
if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL)
{
DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
"UIConstraints!");
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;
}
consts->num_constraints = 2;
consts->constraints = constptr;
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);
}
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;
}
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;
}
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);
}
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;
}
consts->installable = constptr[0].installable || constptr[1].installable;
cupsArrayAdd(ppd->cups_uiconstraints, consts);
}
for (constattr = ppdFindAttr(ppd, "cupsUIConstraints", NULL);
constattr;
constattr = ppdFindNextAttr(ppd, "cupsUIConstraints", NULL))
{
if (!constattr->value)
{
DEBUG_puts("8ppd_load_constraints: Bad cupsUIConstraints value!");
continue;
}
for (i = 0, vptr = strchr(constattr->value, '*');
vptr;
i ++, vptr = strchr(vptr + 1, '*'));
if (i == 0)
{
DEBUG_puts("8ppd_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 "
"cupsUIConstraints!");
return;
}
if ((constptr = calloc(i, sizeof(_ppd_cups_uiconst_t))) == NULL)
{
free(consts);
DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
"cupsUIConstraints!");
return;
}
consts->num_constraints = i;
consts->constraints = constptr;
strlcpy(consts->resolver, constattr->spec, sizeof(consts->resolver));
for (i = 0, vptr = strchr(constattr->value, '*');
vptr;
i ++, vptr = strchr(vptr, '*'), constptr ++)
{
for (vptr ++, ptr = option; *vptr && !_cups_isspace(*vptr); vptr ++)
if (ptr < (option + sizeof(option) - 1))
*ptr++ = *vptr;
*ptr = '\0';
while (_cups_isspace(*vptr))
vptr ++;
if (*vptr == '*')
choice[0] = '\0';
else
{
for (ptr = choice; *vptr && !_cups_isspace(*vptr); vptr ++)
if (ptr < (choice + sizeof(choice) - 1))
*ptr++ = *vptr;
*ptr = '\0';
}
if (!strncasecmp(option, "Custom", 6) && !strcasecmp(choice, "True"))
{
_cups_strcpy(option, option + 6);
strcpy(choice, "Custom");
}
constptr->option = ppdFindOption(ppd, option);
constptr->choice = ppdFindChoice(constptr->option, choice);
constptr->installable = ppd_is_installable(installable, option);
consts->installable |= constptr->installable;
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);
}
}
}
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)
{
int i;
_ppd_cups_uiconsts_t *consts;
_ppd_cups_uiconst_t *constptr;
ppd_choice_t key,
*marked;
cups_array_t *active = NULL;
const char *value;
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))
{
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;
if (!consts->installable && which == _PPD_INSTALLABLE_CONSTRAINTS)
continue;
if (which == _PPD_OPTION_CONSTRAINTS && option)
{
for (i = consts->num_constraints, constptr = consts->constraints;
i > 0;
i --, constptr ++)
if (!strcasecmp(constptr->option->keyword, option))
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")))
{
if (option && choice &&
(!strcasecmp(option, "PageSize") ||
!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)
{
ppd_size_t *size = ppdPageSize(ppd, NULL);
if (size)
value = size->name;
}
if (value && !strncasecmp(value, "Custom.", 7))
value = "Custom";
if (!value || strcasecmp(value, constptr->choice->choice))
{
DEBUG_puts("9ppd_test_constraints: NO");
break;
}
}
else if (constptr->choice)
{
if (option && choice && !strcasecmp(option, constptr->option->keyword))
{
if (!strncasecmp(choice, "Custom.", 7))
value = "Custom";
else
value = choice;
if (strcasecmp(value, constptr->choice->choice))
{
DEBUG_puts("9ppd_test_constraints: NO");
break;
}
}
else if ((value = cupsGetOption(constptr->option->keyword, num_options,
options)) != NULL)
{
if (!strncasecmp(value, "Custom.", 7))
value = "Custom";
if (strcasecmp(value, constptr->choice->choice))
{
DEBUG_puts("9ppd_test_constraints: NO");
break;
}
}
else if (!constptr->choice->marked)
{
DEBUG_puts("9ppd_test_constraints: NO");
break;
}
}
else if (option && choice &&
!strcasecmp(option, constptr->option->keyword))
{
if (!strcasecmp(choice, "None") || !strcasecmp(choice, "Off") ||
!strcasecmp(choice, "False"))
{
DEBUG_puts("9ppd_test_constraints: NO");
break;
}
}
else if ((value = cupsGetOption(constptr->option->keyword, num_options,
options)) != NULL)
{
if (!strcasecmp(value, "None") || !strcasecmp(value, "Off") ||
!strcasecmp(value, "False"))
{
DEBUG_puts("9ppd_test_constraints: NO");
break;
}
}
else
{
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")))
{
DEBUG_puts("9ppd_test_constraints: NO");
break;
}
}
}
if (i <= 0)
{
if (!active)
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);
}