#include "cups.h"
#include <stdlib.h>
#include <ctype.h>
#include "string.h"
#include "debug.h"
#ifdef DEBUG
static void debug_marked(ppd_file_t *ppd, const char *title);
#else
# define debug_marked(ppd,title)
#endif
static int ppd_mark_choices(ppd_file_t *ppd, const char *options);
int
cupsAddOption(const char *name,
const char *value,
int num_options,
cups_option_t **options)
{
int i;
cups_option_t *temp;
if (name == NULL || !name[0] || value == NULL ||
options == NULL || num_options < 0)
return (num_options);
for (i = 0, temp = *options; i < num_options; i ++, temp ++)
if (strcasecmp(temp->name, name) == 0)
break;
if (i >= num_options)
{
if (num_options == 0)
temp = (cups_option_t *)malloc(sizeof(cups_option_t));
else
temp = (cups_option_t *)realloc(*options, sizeof(cups_option_t) *
(num_options + 1));
if (temp == NULL)
return (0);
*options = temp;
temp += num_options;
temp->name = strdup(name);
num_options ++;
}
else
{
free(temp->value);
}
temp->value = strdup(value);
return (num_options);
}
void
cupsFreeOptions(
int num_options,
cups_option_t *options)
{
int i;
if (num_options <= 0 || options == NULL)
return;
for (i = 0; i < num_options; i ++)
{
free(options[i].name);
free(options[i].value);
}
free(options);
}
const char *
cupsGetOption(const char *name,
int num_options,
cups_option_t *options)
{
int i;
if (name == NULL || num_options <= 0 || options == NULL)
return (NULL);
for (i = 0; i < num_options; i ++)
if (strcasecmp(options[i].name, name) == 0)
return (options[i].value);
return (NULL);
}
int
cupsMarkOptions(
ppd_file_t *ppd,
int num_options,
cups_option_t *options)
{
int i, j, k;
int conflict;
char *val,
*ptr,
s[255];
const char *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 == NULL || num_options <= 0 || options == NULL)
return (0);
debug_marked(ppd, "Before...");
conflict = 0;
for (i = num_options, optptr = options; i > 0; i --, optptr ++)
if (!strcasecmp(optptr->name, "media"))
{
page_size = cupsGetOption("PageSize", num_options, options);
for (val = optptr->value; *val;)
{
for (ptr = s; *val && *val != ',' && (ptr - s) < (sizeof(s) - 1);)
*ptr++ = *val++;
*ptr++ = '\0';
if (*val == ',')
val ++;
if (!page_size || !page_size[0])
if (ppdMarkOption(ppd, "PageSize", s))
conflict = 1;
if (cupsGetOption("InputSlot", num_options, options) == NULL)
if (ppdMarkOption(ppd, "InputSlot", s))
conflict = 1;
if (cupsGetOption("MediaType", num_options, options) == NULL)
if (ppdMarkOption(ppd, "MediaType", s))
conflict = 1;
if (cupsGetOption("EFMediaType", num_options, options) == NULL)
if (ppdMarkOption(ppd, "EFMediaType", s))
conflict = 1;
if (cupsGetOption("EFMediaQualityMode", num_options, options) == NULL)
if (ppdMarkOption(ppd, "EFMediaQualityMode", s))
conflict = 1;
if (strcasecmp(s, "manual") == 0 &&
cupsGetOption("ManualFeed", num_options, options) == NULL)
if (ppdMarkOption(ppd, "ManualFeed", "True"))
conflict = 1;
}
}
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) != NULL)
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]))
{
if (ppdMarkOption(ppd, duplex_options[j], duplex_one[k]))
conflict = 1;
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]))
{
if (ppdMarkOption(ppd, duplex_options[j], duplex_two_long[k]))
conflict = 1;
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]))
{
if (ppdMarkOption(ppd, duplex_options[j], duplex_two_short[k]))
conflict = 1;
break;
}
}
}
}
else if (!strcasecmp(optptr->name, "resolution") ||
!strcasecmp(optptr->name, "printer-resolution"))
{
if (ppdMarkOption(ppd, "Resolution", optptr->value))
conflict = 1;
if (ppdMarkOption(ppd, "SetResolution", optptr->value))
conflict = 1;
if (ppdMarkOption(ppd, "JCLResolution", optptr->value))
conflict = 1;
if (ppdMarkOption(ppd, "CNRes_PGP", optptr->value))
conflict = 1;
}
else if (!strcasecmp(optptr->name, "output-bin"))
{
if (!cupsGetOption("OutputBin", num_options, options))
if (ppdMarkOption(ppd, "OutputBin", optptr->value))
conflict = 1;
}
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"))
{
if (ppdMarkOption(ppd, "Collate", "True"))
conflict = 1;
}
else
{
if (ppdMarkOption(ppd, "Collate", "False"))
conflict = 1;
}
}
}
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;
if (ppd_mark_choices(ppd, attr->value))
conflict = 1;
}
}
else if (!strcasecmp(optptr->name, "mirror"))
{
if (ppdMarkOption(ppd, "MirrorPrint", optptr->value))
conflict = 1;
}
else if (ppdMarkOption(ppd, optptr->name, optptr->value))
conflict = 1;
debug_marked(ppd, "After...");
return (conflict);
}
int
cupsParseOptions(
const char *arg,
int num_options,
cups_option_t **options)
{
char *copyarg,
*ptr,
*name,
*value;
if (arg == NULL || options == NULL || num_options < 0)
return (0);
copyarg = strdup(arg);
ptr = copyarg;
while (isspace(*ptr & 255))
ptr ++;
while (*ptr != '\0')
{
name = ptr;
while (!isspace(*ptr & 255) && *ptr != '=' && *ptr != '\0')
ptr ++;
if (ptr == name)
break;
while (isspace(*ptr & 255))
*ptr++ = '\0';
if (*ptr != '=')
{
if (strncasecmp(name, "no", 2) == 0)
num_options = cupsAddOption(name + 2, "false", num_options,
options);
else
num_options = cupsAddOption(name, "true", num_options, options);
continue;
}
*ptr++ = '\0';
if (*ptr == '\'')
{
ptr ++;
value = ptr;
while (*ptr != '\'' && *ptr != '\0')
{
if (*ptr == '\\')
_cups_strcpy(ptr, ptr + 1);
ptr ++;
}
if (*ptr != '\0')
*ptr++ = '\0';
}
else if (*ptr == '\"')
{
ptr ++;
value = ptr;
while (*ptr != '\"' && *ptr != '\0')
{
if (*ptr == '\\')
_cups_strcpy(ptr, ptr + 1);
ptr ++;
}
if (*ptr != '\0')
*ptr++ = '\0';
}
else if (*ptr == '{')
{
int depth;
value = ptr;
for (depth = 1; *ptr; ptr ++)
if (*ptr == '{')
depth ++;
else if (*ptr == '}')
{
depth --;
if (!depth)
{
ptr ++;
if (*ptr != ',')
break;
}
}
else if (*ptr == '\\')
_cups_strcpy(ptr, ptr + 1);
if (*ptr != '\0')
*ptr++ = '\0';
}
else
{
value = ptr;
while (!isspace(*ptr & 255) && *ptr != '\0')
{
if (*ptr == '\\')
_cups_strcpy(ptr, ptr + 1);
ptr ++;
}
}
while (isspace(*ptr & 255))
*ptr++ = '\0';
num_options = cupsAddOption(name, value, num_options, options);
}
free(copyarg);
return (num_options);
}
int
cupsRemoveOption(
const char *name,
int num_options,
cups_option_t **options)
{
int i;
cups_option_t *option;
if (!name || num_options < 1 || !options)
return (num_options);
for (i = num_options, option = *options; i > 0; i --, option ++)
if (!strcasecmp(name, option->name))
break;
if (i)
{
num_options --;
i --;
free(option->name);
if (option->value)
free(option->value);
if (i > 0)
memmove(option, option + 1, i * sizeof(cups_option_t));
}
return (num_options);
}
#ifdef DEBUG
static void
debug_marked(ppd_file_t *ppd,
const char *title)
{
ppd_choice_t *c;
printf("cupsMarkOptions: %s\n", title);
for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
c;
c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
printf("cupsMarkOptions: %s=%s\n", c->option->keyword, c->choice);
}
#endif
static int
ppd_mark_choices(ppd_file_t *ppd,
const char *options)
{
char option[PPD_MAX_NAME],
choice[PPD_MAX_NAME],
*ptr;
int conflict = 0;
if (!options)
return (0);
while (*options)
{
while (isspace(*options & 255))
options ++;
if (*options != '*')
break;
options ++;
ptr = option;
while (*options && !isspace(*options & 255) &&
ptr < (option + sizeof(option) - 1))
*ptr++ = *options++;
if (ptr == option)
break;
*ptr = '\0';
while (isspace(*options & 255))
options ++;
if (!*options)
break;
ptr = choice;
while (*options && !isspace(*options & 255) &&
ptr < (choice + sizeof(choice) - 1))
*ptr++ = *options++;
*ptr = '\0';
if (ppdMarkOption(ppd, option, choice))
conflict = 1;
}
return (conflict);
}