#include "cups.h"
#include "string.h"
#include "debug.h"
#include "pwgmedia.h"
#ifdef DEBUG
static void debug_marked(ppd_file_t *ppd, const char *title);
#else
# define debug_marked(ppd,title)
#endif
static void ppd_defaults(ppd_file_t *ppd, ppd_group_t *g);
static void ppd_mark_choices(ppd_file_t *ppd, const char *s);
static void ppd_mark_option(ppd_file_t *ppd, const char *option,
const char *choice);
static void ppd_mark_size(ppd_file_t *ppd, const char *size);
int
cupsMarkOptions(
ppd_file_t *ppd,
int num_options,
cups_option_t *options)
{
int i, j, k;
char *ptr,
s[255];
const char *val,
*media,
*media_col,
*page_size;
cups_option_t *optptr;
ppd_option_t *option;
ppd_attr_t *attr;
static const char * const duplex_options[] =
{
"Duplex",
"EFDuplex",
"EFDuplexing",
"KD03Duplex",
"JCLDuplex"
};
static const char * const duplex_one[] =
{
"None",
"False"
};
static const char * const duplex_two_long[] =
{
"DuplexNoTumble",
"LongEdge",
"Top"
};
static const char * const duplex_two_short[] =
{
"DuplexTumble",
"ShortEdge",
"Bottom"
};
if (!ppd || num_options <= 0 || !options)
return (0);
debug_marked(ppd, "Before...");
media = cupsGetOption("media", num_options, options);
media_col = cupsGetOption("media-col", num_options, options);
page_size = cupsGetOption("PageSize", num_options, options);
if (media_col && (!page_size || !page_size[0]))
{
int num_media_cols,
num_media_sizes;
cups_option_t *media_cols,
*media_sizes;
num_media_cols = cupsParseOptions(media_col, 0, &media_cols);
if ((val = cupsGetOption("media-key", num_media_cols, media_cols)) != NULL)
media = val;
else if ((val = cupsGetOption("media-size", num_media_cols,
media_cols)) != NULL)
{
double width,
length;
struct lconv *loc;
_cups_pwg_media_t *pwgmedia;
num_media_sizes = cupsParseOptions(val, 0, &media_sizes);
loc = localeconv();
if ((val = cupsGetOption("x-dimension", num_media_sizes,
media_sizes)) != NULL)
width = _cupsStrScand(val, NULL, loc) * 2540.0 / 72.0;
else
width = 0.0;
if ((val = cupsGetOption("y-dimension", num_media_sizes,
media_sizes)) != NULL)
length = _cupsStrScand(val, NULL, loc) * 2540.0 / 72.0;
else
length = 0.0;
if ((pwgmedia = _cupsPWGMediaBySize(width, length)) != NULL)
media = pwgmedia->pwg;
cupsFreeOptions(num_media_sizes, media_sizes);
}
cupsFreeOptions(num_media_cols, media_cols);
}
if (media)
{
for (val = media; *val;)
{
for (ptr = s; *val && *val != ',' && (ptr - s) < (sizeof(s) - 1);)
*ptr++ = *val++;
*ptr++ = '\0';
if (*val == ',')
val ++;
if (!page_size || !page_size[0])
ppd_mark_size(ppd, s);
if (cupsGetOption("InputSlot", num_options, options) == NULL)
ppd_mark_option(ppd, "InputSlot", s);
if (cupsGetOption("MediaType", num_options, options) == NULL)
ppd_mark_option(ppd, "MediaType", s);
if (cupsGetOption("EFMediaType", num_options, options) == NULL)
ppd_mark_option(ppd, "EFMediaType", s);
if (cupsGetOption("EFMediaQualityMode", num_options, options) == NULL)
ppd_mark_option(ppd, "EFMediaQualityMode", s);
if (!strcasecmp(s, "manual") &&
!cupsGetOption("ManualFeed", num_options, options))
ppd_mark_option(ppd, "ManualFeed", "True");
}
}
for (i = num_options, optptr = options; i > 0; i --, optptr ++)
if (!strcasecmp(optptr->name, "media") ||
!strcasecmp(optptr->name, "media-col"))
continue;
else if (!strcasecmp(optptr->name, "sides"))
{
for (j = 0;
j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0]));
j ++)
if (cupsGetOption(duplex_options[j], num_options, options))
break;
if (j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0])))
{
continue;
}
if (!strcasecmp(optptr->value, "one-sided"))
{
for (j = 0;
j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0]));
j ++)
if ((option = ppdFindOption(ppd, duplex_options[j])) != NULL)
break;
if (j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0])))
{
for (k = 0;
k < (int)(sizeof(duplex_one) / sizeof(duplex_one[0]));
k ++)
if (ppdFindChoice(option, duplex_one[k]))
{
ppd_mark_option(ppd, duplex_options[j], duplex_one[k]);
break;
}
}
}
else if (!strcasecmp(optptr->value, "two-sided-long-edge"))
{
for (j = 0;
j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0]));
j ++)
if ((option = ppdFindOption(ppd, duplex_options[j])) != NULL)
break;
if (j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0])))
{
for (k = 0;
k < (int)(sizeof(duplex_two_long) / sizeof(duplex_two_long[0]));
k ++)
if (ppdFindChoice(option, duplex_two_long[k]))
{
ppd_mark_option(ppd, duplex_options[j], duplex_two_long[k]);
break;
}
}
}
else if (!strcasecmp(optptr->value, "two-sided-short-edge"))
{
for (j = 0;
j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0]));
j ++)
if ((option = ppdFindOption(ppd, duplex_options[j])) != NULL)
break;
if (j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0])))
{
for (k = 0;
k < (int)(sizeof(duplex_two_short) / sizeof(duplex_two_short[0]));
k ++)
if (ppdFindChoice(option, duplex_two_short[k]))
{
ppd_mark_option(ppd, duplex_options[j], duplex_two_short[k]);
break;
}
}
}
}
else if (!strcasecmp(optptr->name, "resolution") ||
!strcasecmp(optptr->name, "printer-resolution"))
{
ppd_mark_option(ppd, "Resolution", optptr->value);
ppd_mark_option(ppd, "SetResolution", optptr->value);
ppd_mark_option(ppd, "JCLResolution", optptr->value);
ppd_mark_option(ppd, "CNRes_PGP", optptr->value);
}
else if (!strcasecmp(optptr->name, "output-bin"))
{
if (!cupsGetOption("OutputBin", num_options, options))
ppd_mark_option(ppd, "OutputBin", optptr->value);
}
else if (!strcasecmp(optptr->name, "multiple-document-handling"))
{
if (!cupsGetOption("Collate", num_options, options) &&
ppdFindOption(ppd, "Collate"))
{
if (strcasecmp(optptr->value, "separate-documents-uncollated-copies"))
ppd_mark_option(ppd, "Collate", "True");
else
ppd_mark_option(ppd, "Collate", "False");
}
}
else if (!strcasecmp(optptr->name, "finishings"))
{
for (ptr = optptr->value; *ptr;)
{
if (!isdigit(*ptr & 255))
break;
if ((j = strtol(ptr, &ptr, 10)) < 3)
break;
if (*ptr == ',')
ptr ++;
sprintf(s, "%d", j);
if ((attr = ppdFindAttr(ppd, "cupsIPPFinishings", s)) == NULL)
continue;
ppd_mark_choices(ppd, attr->value);
}
}
else if (!strcasecmp(optptr->name, "APPrinterPreset"))
{
if ((attr = ppdFindAttr(ppd, "APPrinterPreset", optptr->value)) != NULL)
{
ppd_mark_choices(ppd, attr->value);
}
}
else if (!strcasecmp(optptr->name, "mirror"))
ppd_mark_option(ppd, "MirrorPrint", optptr->value);
else
ppd_mark_option(ppd, optptr->name, optptr->value);
debug_marked(ppd, "After...");
return (ppdConflicts(ppd) > 0);
}
ppd_choice_t *
ppdFindChoice(ppd_option_t *o,
const char *choice)
{
int i;
ppd_choice_t *c;
if (!o || !choice)
return (NULL);
if (choice[0] == '{' || !strncasecmp(choice, "Custom.", 7))
choice = "Custom";
for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
if (!strcasecmp(c->choice, choice))
return (c);
return (NULL);
}
ppd_choice_t *
ppdFindMarkedChoice(ppd_file_t *ppd,
const char *option)
{
ppd_choice_t key,
*marked;
DEBUG_printf(("2ppdFindMarkedChoice(ppd=%p, option=\"%s\")", ppd, option));
if ((key.option = ppdFindOption(ppd, option)) == NULL)
{
DEBUG_puts("3ppdFindMarkedChoice: Option not found, returning NULL");
return (NULL);
}
marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key);
DEBUG_printf(("3ppdFindMarkedChoice: Returning %p(%s)...", marked,
marked ? marked->choice : "NULL"));
return (marked);
}
ppd_option_t *
ppdFindOption(ppd_file_t *ppd,
const char *option)
{
if (!ppd || !option)
return (NULL);
if (ppd->options)
{
ppd_option_t key;
strlcpy(key.keyword, option, sizeof(key.keyword));
return ((ppd_option_t *)cupsArrayFind(ppd->options, &key));
}
else
{
int i, j;
ppd_group_t *group;
ppd_option_t *optptr;
for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
for (j = group->num_options, optptr = group->options;
j > 0;
j --, optptr ++)
if (!strcasecmp(optptr->keyword, option))
return (optptr);
return (NULL);
}
}
int
ppdIsMarked(ppd_file_t *ppd,
const char *option,
const char *choice)
{
ppd_choice_t key,
*c;
if (!ppd)
return (0);
if ((key.option = ppdFindOption(ppd, option)) == NULL)
return (0);
if ((c = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) == NULL)
return (0);
return (!strcmp(c->choice, choice));
}
void
ppdMarkDefaults(ppd_file_t *ppd)
{
int i;
ppd_group_t *g;
ppd_choice_t *c;
if (!ppd)
return;
for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
c;
c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
cupsArrayRemove(ppd->marked, c);
for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++)
ppd_defaults(ppd, g);
}
int
ppdMarkOption(ppd_file_t *ppd,
const char *option,
const char *choice)
{
DEBUG_printf(("ppdMarkOption(ppd=%p, option=\"%s\", choice=\"%s\")",
ppd, option, choice));
if (!ppd || !option || !choice)
return (0);
ppd_mark_option(ppd, option, choice);
return (ppdConflicts(ppd));
}
ppd_option_t *
ppdFirstOption(ppd_file_t *ppd)
{
if (!ppd)
return (NULL);
else
return ((ppd_option_t *)cupsArrayFirst(ppd->options));
}
ppd_option_t *
ppdNextOption(ppd_file_t *ppd)
{
if (!ppd)
return (NULL);
else
return ((ppd_option_t *)cupsArrayNext(ppd->options));
}
int
_ppdParseOptions(
const char *s,
int num_options,
cups_option_t **options)
{
char option[PPD_MAX_NAME],
choice[PPD_MAX_NAME],
*ptr;
if (!s)
return (num_options);
while (*s)
{
while (isspace(*s & 255))
s ++;
if (*s != '*')
break;
s ++;
ptr = option;
while (*s && !isspace(*s & 255) && ptr < (option + sizeof(option) - 1))
*ptr++ = *s++;
if (ptr == s)
break;
*ptr = '\0';
while (isspace(*s & 255))
s ++;
if (!*s)
break;
ptr = choice;
while (*s && !isspace(*s & 255) && ptr < (choice + sizeof(choice) - 1))
*ptr++ = *s++;
*ptr = '\0';
num_options = cupsAddOption(option, choice, num_options, options);
}
return (num_options);
}
#ifdef DEBUG
static void
debug_marked(ppd_file_t *ppd,
const char *title)
{
ppd_choice_t *c;
DEBUG_printf(("2cupsMarkOptions: %s", title));
for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
c;
c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
DEBUG_printf(("2cupsMarkOptions: %s=%s", c->option->keyword, c->choice));
}
#endif
static void
ppd_defaults(ppd_file_t *ppd,
ppd_group_t *g)
{
int i;
ppd_option_t *o;
ppd_group_t *sg;
for (i = g->num_options, o = g->options; i > 0; i --, o ++)
if (strcasecmp(o->keyword, "PageRegion") != 0)
ppdMarkOption(ppd, o->keyword, o->defchoice);
for (i = g->num_subgroups, sg = g->subgroups; i > 0; i --, sg ++)
ppd_defaults(ppd, sg);
}
static void
ppd_mark_choices(ppd_file_t *ppd,
const char *s)
{
int i,
num_options;
cups_option_t *options,
*option;
if (!s)
return;
options = NULL;
num_options = _ppdParseOptions(s, 0, &options);
for (i = num_options, option = options; i > 0; i --, option ++)
ppd_mark_option(ppd, option->name, option->value);
cupsFreeOptions(num_options, options);
}
static void
ppd_mark_option(ppd_file_t *ppd,
const char *option,
const char *choice)
{
int i, j;
ppd_option_t *o;
ppd_choice_t *c,
*oldc,
key;
struct lconv *loc;
DEBUG_printf(("7ppd_mark_option(ppd=%p, option=\"%s\", choice=\"%s\")",
ppd, option, choice));
if (!strcasecmp(option, "AP_D_InputSlot"))
{
if ((o = ppdFindOption(ppd, "InputSlot")) != NULL)
{
key.option = o;
if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
{
oldc->marked = 0;
cupsArrayRemove(ppd->marked, oldc);
}
}
}
if ((o = ppdFindOption(ppd, option)) == NULL)
return;
loc = localeconv();
if (!strncasecmp(choice, "Custom.", 7))
{
if ((c = ppdFindChoice(o, "Custom")) == NULL)
return;
if (!strcasecmp(option, "PageSize"))
{
ppdPageSize(ppd, choice);
}
else
{
ppd_coption_t *coption;
ppd_cparam_t *cparam;
char *units;
if ((coption = ppdFindCustomOption(ppd, option)) != NULL)
{
if ((cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params)) == NULL)
return;
switch (cparam->type)
{
case PPD_CUSTOM_CURVE :
case PPD_CUSTOM_INVCURVE :
case PPD_CUSTOM_REAL :
cparam->current.custom_real = (float)_cupsStrScand(choice + 7,
NULL, loc);
break;
case PPD_CUSTOM_POINTS :
cparam->current.custom_points = (float)_cupsStrScand(choice + 7,
&units,
loc);
if (units)
{
if (!strcasecmp(units, "cm"))
cparam->current.custom_points *= 72.0f / 2.54f;
else if (!strcasecmp(units, "mm"))
cparam->current.custom_points *= 72.0f / 25.4f;
else if (!strcasecmp(units, "m"))
cparam->current.custom_points *= 72.0f / 0.0254f;
else if (!strcasecmp(units, "in"))
cparam->current.custom_points *= 72.0f;
else if (!strcasecmp(units, "ft"))
cparam->current.custom_points *= 12.0f * 72.0f;
}
break;
case PPD_CUSTOM_INT :
cparam->current.custom_int = atoi(choice + 7);
break;
case PPD_CUSTOM_PASSCODE :
case PPD_CUSTOM_PASSWORD :
case PPD_CUSTOM_STRING :
if (cparam->current.custom_string)
_cupsStrFree(cparam->current.custom_string);
cparam->current.custom_string = _cupsStrAlloc(choice + 7);
break;
}
}
}
choice = "Custom";
}
else if (choice[0] == '{')
{
ppd_coption_t *coption;
ppd_cparam_t *cparam;
char *units;
int num_vals;
cups_option_t *vals,
*val;
if ((c = ppdFindChoice(o, "Custom")) == NULL)
return;
if ((coption = ppdFindCustomOption(ppd, option)) != NULL)
{
num_vals = cupsParseOptions(choice, 0, &vals);
for (i = 0, val = vals; i < num_vals; i ++, val ++)
{
if ((cparam = ppdFindCustomParam(coption, val->name)) == NULL)
continue;
switch (cparam->type)
{
case PPD_CUSTOM_CURVE :
case PPD_CUSTOM_INVCURVE :
case PPD_CUSTOM_REAL :
cparam->current.custom_real = (float)_cupsStrScand(val->value,
NULL, loc);
break;
case PPD_CUSTOM_POINTS :
cparam->current.custom_points = (float)_cupsStrScand(val->value,
&units,
loc);
if (units)
{
if (!strcasecmp(units, "cm"))
cparam->current.custom_points *= 72.0f / 2.54f;
else if (!strcasecmp(units, "mm"))
cparam->current.custom_points *= 72.0f / 25.4f;
else if (!strcasecmp(units, "m"))
cparam->current.custom_points *= 72.0f / 0.0254f;
else if (!strcasecmp(units, "in"))
cparam->current.custom_points *= 72.0f;
else if (!strcasecmp(units, "ft"))
cparam->current.custom_points *= 12.0f * 72.0f;
}
break;
case PPD_CUSTOM_INT :
cparam->current.custom_int = atoi(val->value);
break;
case PPD_CUSTOM_PASSCODE :
case PPD_CUSTOM_PASSWORD :
case PPD_CUSTOM_STRING :
if (cparam->current.custom_string)
_cupsStrFree(cparam->current.custom_string);
cparam->current.custom_string = _cupsStrRetain(val->value);
break;
}
}
cupsFreeOptions(num_vals, vals);
}
}
else
{
for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
if (!strcasecmp(c->choice, choice))
break;
if (!i)
return;
}
if (o->ui != PPD_UI_PICKMANY)
{
if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, c)) != NULL)
{
oldc->marked = 0;
cupsArrayRemove(ppd->marked, oldc);
}
if (!strcasecmp(option, "PageSize") || !strcasecmp(option, "PageRegion"))
{
for (j = 0; j < ppd->num_sizes; j ++)
ppd->sizes[j].marked = !strcasecmp(ppd->sizes[j].name,
choice);
if (!strcasecmp(option, "PageSize"))
{
if ((o = ppdFindOption(ppd, "PageRegion")) != NULL)
{
key.option = o;
if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
{
oldc->marked = 0;
cupsArrayRemove(ppd->marked, oldc);
}
}
}
else
{
if ((o = ppdFindOption(ppd, "PageSize")) != NULL)
{
key.option = o;
if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
{
oldc->marked = 0;
cupsArrayRemove(ppd->marked, oldc);
}
}
}
}
else if (!strcasecmp(option, "InputSlot"))
{
if ((o = ppdFindOption(ppd, "ManualFeed")) != NULL)
{
key.option = o;
if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
{
oldc->marked = 0;
cupsArrayRemove(ppd->marked, oldc);
}
}
}
else if (!strcasecmp(option, "ManualFeed") &&
!strcasecmp(choice, "True"))
{
if ((o = ppdFindOption(ppd, "InputSlot")) != NULL)
{
key.option = o;
if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
{
oldc->marked = 0;
cupsArrayRemove(ppd->marked, oldc);
}
}
}
}
c->marked = 1;
cupsArrayAdd(ppd->marked, c);
}
static void
ppd_mark_size(ppd_file_t *ppd,
const char *size)
{
int i;
_cups_pwg_media_t *pwgmedia;
ppd_size_t *ppdsize;
double dw, dl;
double width,
length;
char width_str[256],
length_str[256],
units[256],
custom[256];
struct lconv *loc;
if (!strncasecmp(size, "Custom.", 7) || ppdPageSize(ppd, size))
{
ppd_mark_option(ppd, "PageSize", size);
return;
}
if ((pwgmedia = _cupsPWGMediaByName(size)) == NULL)
pwgmedia = _cupsPWGMediaByLegacy(size);
if (pwgmedia)
{
width = pwgmedia->width;
length = pwgmedia->length;
}
else if (sscanf(size, "%*[^_]_%*[^_]_%255[0-9.]x%255[0-9.]%s", width_str,
length_str, units) == 3)
{
loc = localeconv();
width = _cupsStrScand(width_str, NULL, loc);
length = _cupsStrScand(length_str, NULL, loc);
if (!strcmp(units, "in"))
{
width *= 72.0;
length *= 72.0;
}
else if (!strcmp(units, "mm"))
{
width *= 25.4 / 72.0;
length *= 25.4 / 72.0;
}
else
return;
}
else
return;
for (i = ppd->num_sizes, ppdsize = ppd->sizes; i > 0; i --, ppdsize ++)
{
dw = ppdsize->width - width;
dl = ppdsize->length - length;
if (dw > -5.0 && dw < 5.0 && dl > -5.0 && dl < 5.0)
{
ppd_mark_option(ppd, "PageSize", ppdsize->name);
return;
}
}
if (ppd->variable_sizes)
{
snprintf(custom, sizeof(custom), "Custom.%dx%d", (int)width, (int)length);
ppd_mark_option(ppd, "PageSize", custom);
}
}