You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
742 lines
16 KiB
742 lines
16 KiB
/*
|
|
* Option routines for CUPS.
|
|
*
|
|
* Copyright 2007-2017 by Apple Inc.
|
|
* Copyright 1997-2007 by Easy Software Products.
|
|
*
|
|
* Licensed under Apache License v2.0. See the file "LICENSE" for more information.
|
|
*/
|
|
|
|
/*
|
|
* Include necessary headers...
|
|
*/
|
|
|
|
#include "cups-private.h"
|
|
#include "debug-internal.h"
|
|
|
|
|
|
/*
|
|
* Local functions...
|
|
*/
|
|
|
|
static int cups_compare_options(cups_option_t *a, cups_option_t *b);
|
|
static int cups_find_option(const char *name, int num_options,
|
|
cups_option_t *option, int prev, int *rdiff);
|
|
|
|
|
|
/*
|
|
* 'cupsAddIntegerOption()' - Add an integer option to an option array.
|
|
*
|
|
* New option arrays can be initialized simply by passing 0 for the
|
|
* "num_options" parameter.
|
|
*
|
|
* @since CUPS 2.2.4/macOS 10.13@
|
|
*/
|
|
|
|
int /* O - Number of options */
|
|
cupsAddIntegerOption(
|
|
const char *name, /* I - Name of option */
|
|
int value, /* I - Value of option */
|
|
int num_options, /* I - Number of options */
|
|
cups_option_t **options) /* IO - Pointer to options */
|
|
{
|
|
char strvalue[32]; /* String value */
|
|
|
|
|
|
snprintf(strvalue, sizeof(strvalue), "%d", value);
|
|
|
|
return (cupsAddOption(name, strvalue, num_options, options));
|
|
}
|
|
|
|
|
|
/*
|
|
* 'cupsAddOption()' - Add an option to an option array.
|
|
*
|
|
* New option arrays can be initialized simply by passing 0 for the
|
|
* "num_options" parameter.
|
|
*/
|
|
|
|
int /* O - Number of options */
|
|
cupsAddOption(const char *name, /* I - Name of option */
|
|
const char *value, /* I - Value of option */
|
|
int num_options,/* I - Number of options */
|
|
cups_option_t **options) /* IO - Pointer to options */
|
|
{
|
|
cups_option_t *temp; /* Pointer to new option */
|
|
int insert, /* Insertion point */
|
|
diff; /* Result of search */
|
|
|
|
|
|
DEBUG_printf(("2cupsAddOption(name=\"%s\", value=\"%s\", num_options=%d, options=%p)", name, value, num_options, (void *)options));
|
|
|
|
if (!name || !name[0] || !value || !options || num_options < 0)
|
|
{
|
|
DEBUG_printf(("3cupsAddOption: Returning %d", num_options));
|
|
return (num_options);
|
|
}
|
|
|
|
if (!_cups_strcasecmp(name, "cupsPrintQuality"))
|
|
num_options = cupsRemoveOption("print-quality", num_options, options);
|
|
else if (!_cups_strcasecmp(name, "print-quality"))
|
|
num_options = cupsRemoveOption("cupsPrintQuality", num_options, options);
|
|
|
|
/*
|
|
* Look for an existing option with the same name...
|
|
*/
|
|
|
|
if (num_options == 0)
|
|
{
|
|
insert = 0;
|
|
diff = 1;
|
|
}
|
|
else
|
|
{
|
|
insert = cups_find_option(name, num_options, *options, num_options - 1,
|
|
&diff);
|
|
|
|
if (diff > 0)
|
|
insert ++;
|
|
}
|
|
|
|
if (diff)
|
|
{
|
|
/*
|
|
* No matching option name...
|
|
*/
|
|
|
|
DEBUG_printf(("4cupsAddOption: New option inserted at index %d...",
|
|
insert));
|
|
|
|
if (num_options == 0)
|
|
temp = (cups_option_t *)malloc(sizeof(cups_option_t));
|
|
else
|
|
temp = (cups_option_t *)realloc(*options, sizeof(cups_option_t) * (size_t)(num_options + 1));
|
|
|
|
if (!temp)
|
|
{
|
|
DEBUG_puts("3cupsAddOption: Unable to expand option array, returning 0");
|
|
return (0);
|
|
}
|
|
|
|
*options = temp;
|
|
|
|
if (insert < num_options)
|
|
{
|
|
DEBUG_printf(("4cupsAddOption: Shifting %d options...",
|
|
(int)(num_options - insert)));
|
|
memmove(temp + insert + 1, temp + insert, (size_t)(num_options - insert) * sizeof(cups_option_t));
|
|
}
|
|
|
|
temp += insert;
|
|
temp->name = _cupsStrAlloc(name);
|
|
num_options ++;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Match found; free the old value...
|
|
*/
|
|
|
|
DEBUG_printf(("4cupsAddOption: Option already exists at index %d...",
|
|
insert));
|
|
|
|
temp = *options + insert;
|
|
_cupsStrFree(temp->value);
|
|
}
|
|
|
|
temp->value = _cupsStrAlloc(value);
|
|
|
|
DEBUG_printf(("3cupsAddOption: Returning %d", num_options));
|
|
|
|
return (num_options);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'cupsFreeOptions()' - Free all memory used by options.
|
|
*/
|
|
|
|
void
|
|
cupsFreeOptions(
|
|
int num_options, /* I - Number of options */
|
|
cups_option_t *options) /* I - Pointer to options */
|
|
{
|
|
int i; /* Looping var */
|
|
|
|
|
|
DEBUG_printf(("cupsFreeOptions(num_options=%d, options=%p)", num_options, (void *)options));
|
|
|
|
if (num_options <= 0 || !options)
|
|
return;
|
|
|
|
for (i = 0; i < num_options; i ++)
|
|
{
|
|
_cupsStrFree(options[i].name);
|
|
_cupsStrFree(options[i].value);
|
|
}
|
|
|
|
free(options);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'cupsGetIntegerOption()' - Get an integer option value.
|
|
*
|
|
* INT_MIN is returned when the option does not exist, is not an integer, or
|
|
* exceeds the range of values for the "int" type.
|
|
*
|
|
* @since CUPS 2.2.4/macOS 10.13@
|
|
*/
|
|
|
|
int /* O - Option value or @code INT_MIN@ */
|
|
cupsGetIntegerOption(
|
|
const char *name, /* I - Name of option */
|
|
int num_options, /* I - Number of options */
|
|
cups_option_t *options) /* I - Options */
|
|
{
|
|
const char *value = cupsGetOption(name, num_options, options);
|
|
/* String value of option */
|
|
char *ptr; /* Pointer into string value */
|
|
long intvalue; /* Integer value */
|
|
|
|
|
|
if (!value || !*value)
|
|
return (INT_MIN);
|
|
|
|
intvalue = strtol(value, &ptr, 10);
|
|
if (intvalue < INT_MIN || intvalue > INT_MAX || *ptr)
|
|
return (INT_MIN);
|
|
|
|
return ((int)intvalue);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'cupsGetOption()' - Get an option value.
|
|
*/
|
|
|
|
const char * /* O - Option value or @code NULL@ */
|
|
cupsGetOption(const char *name, /* I - Name of option */
|
|
int num_options,/* I - Number of options */
|
|
cups_option_t *options) /* I - Options */
|
|
{
|
|
int diff, /* Result of comparison */
|
|
match; /* Matching index */
|
|
|
|
|
|
DEBUG_printf(("2cupsGetOption(name=\"%s\", num_options=%d, options=%p)", name, num_options, (void *)options));
|
|
|
|
if (!name || num_options <= 0 || !options)
|
|
{
|
|
DEBUG_puts("3cupsGetOption: Returning NULL");
|
|
return (NULL);
|
|
}
|
|
|
|
match = cups_find_option(name, num_options, options, -1, &diff);
|
|
|
|
if (!diff)
|
|
{
|
|
DEBUG_printf(("3cupsGetOption: Returning \"%s\"", options[match].value));
|
|
return (options[match].value);
|
|
}
|
|
|
|
DEBUG_puts("3cupsGetOption: Returning NULL");
|
|
return (NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'cupsParseOptions()' - Parse options from a command-line argument.
|
|
*
|
|
* This function converts space-delimited name/value pairs according
|
|
* to the PAPI text option ABNF specification. Collection values
|
|
* ("name={a=... b=... c=...}") are stored with the curley brackets
|
|
* intact - use @code cupsParseOptions@ on the value to extract the
|
|
* collection attributes.
|
|
*/
|
|
|
|
int /* O - Number of options found */
|
|
cupsParseOptions(
|
|
const char *arg, /* I - Argument to parse */
|
|
int num_options, /* I - Number of options */
|
|
cups_option_t **options) /* O - Options found */
|
|
{
|
|
char *copyarg, /* Copy of input string */
|
|
*ptr, /* Pointer into string */
|
|
*name, /* Pointer to name */
|
|
*value, /* Pointer to value */
|
|
sep, /* Separator character */
|
|
quote; /* Quote character */
|
|
|
|
|
|
DEBUG_printf(("cupsParseOptions(arg=\"%s\", num_options=%d, options=%p)", arg, num_options, (void *)options));
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!arg)
|
|
{
|
|
DEBUG_printf(("1cupsParseOptions: Returning %d", num_options));
|
|
return (num_options);
|
|
}
|
|
|
|
if (!options || num_options < 0)
|
|
{
|
|
DEBUG_puts("1cupsParseOptions: Returning 0");
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Make a copy of the argument string and then divide it up...
|
|
*/
|
|
|
|
if ((copyarg = strdup(arg)) == NULL)
|
|
{
|
|
DEBUG_puts("1cupsParseOptions: Unable to copy arg string");
|
|
DEBUG_printf(("1cupsParseOptions: Returning %d", num_options));
|
|
return (num_options);
|
|
}
|
|
|
|
if (*copyarg == '{')
|
|
{
|
|
/*
|
|
* Remove surrounding {} so we can parse "{name=value ... name=value}"...
|
|
*/
|
|
|
|
if ((ptr = copyarg + strlen(copyarg) - 1) > copyarg && *ptr == '}')
|
|
{
|
|
*ptr = '\0';
|
|
ptr = copyarg + 1;
|
|
}
|
|
else
|
|
ptr = copyarg;
|
|
}
|
|
else
|
|
ptr = copyarg;
|
|
|
|
/*
|
|
* Skip leading spaces...
|
|
*/
|
|
|
|
while (_cups_isspace(*ptr))
|
|
ptr ++;
|
|
|
|
/*
|
|
* Loop through the string...
|
|
*/
|
|
|
|
while (*ptr != '\0')
|
|
{
|
|
/*
|
|
* Get the name up to a SPACE, =, or end-of-string...
|
|
*/
|
|
|
|
name = ptr;
|
|
while (!strchr("\f\n\r\t\v =", *ptr) && *ptr)
|
|
ptr ++;
|
|
|
|
/*
|
|
* Avoid an empty name...
|
|
*/
|
|
|
|
if (ptr == name)
|
|
break;
|
|
|
|
/*
|
|
* Skip trailing spaces...
|
|
*/
|
|
|
|
while (_cups_isspace(*ptr))
|
|
*ptr++ = '\0';
|
|
|
|
if ((sep = *ptr) == '=')
|
|
*ptr++ = '\0';
|
|
|
|
DEBUG_printf(("2cupsParseOptions: name=\"%s\"", name));
|
|
|
|
if (sep != '=')
|
|
{
|
|
/*
|
|
* Boolean option...
|
|
*/
|
|
|
|
if (!_cups_strncasecmp(name, "no", 2))
|
|
num_options = cupsAddOption(name + 2, "false", num_options,
|
|
options);
|
|
else
|
|
num_options = cupsAddOption(name, "true", num_options, options);
|
|
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Remove = and parse the value...
|
|
*/
|
|
|
|
value = ptr;
|
|
|
|
while (*ptr && !_cups_isspace(*ptr))
|
|
{
|
|
if (*ptr == ',')
|
|
ptr ++;
|
|
else if (*ptr == '\'' || *ptr == '\"')
|
|
{
|
|
/*
|
|
* Quoted string constant...
|
|
*/
|
|
|
|
quote = *ptr;
|
|
_cups_strcpy(ptr, ptr + 1);
|
|
|
|
while (*ptr != quote && *ptr)
|
|
{
|
|
if (*ptr == '\\' && ptr[1])
|
|
_cups_strcpy(ptr, ptr + 1);
|
|
|
|
ptr ++;
|
|
}
|
|
|
|
if (*ptr)
|
|
_cups_strcpy(ptr, ptr + 1);
|
|
}
|
|
else if (*ptr == '{')
|
|
{
|
|
/*
|
|
* Collection value...
|
|
*/
|
|
|
|
int depth;
|
|
|
|
for (depth = 0; *ptr; ptr ++)
|
|
{
|
|
if (*ptr == '{')
|
|
depth ++;
|
|
else if (*ptr == '}')
|
|
{
|
|
depth --;
|
|
if (!depth)
|
|
{
|
|
ptr ++;
|
|
break;
|
|
}
|
|
}
|
|
else if (*ptr == '\\' && ptr[1])
|
|
_cups_strcpy(ptr, ptr + 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Normal space-delimited string...
|
|
*/
|
|
|
|
while (*ptr && !_cups_isspace(*ptr))
|
|
{
|
|
if (*ptr == '\\' && ptr[1])
|
|
_cups_strcpy(ptr, ptr + 1);
|
|
|
|
ptr ++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (*ptr != '\0')
|
|
*ptr++ = '\0';
|
|
|
|
DEBUG_printf(("2cupsParseOptions: value=\"%s\"", value));
|
|
|
|
/*
|
|
* Skip trailing whitespace...
|
|
*/
|
|
|
|
while (_cups_isspace(*ptr))
|
|
ptr ++;
|
|
|
|
/*
|
|
* Add the string value...
|
|
*/
|
|
|
|
num_options = cupsAddOption(name, value, num_options, options);
|
|
}
|
|
|
|
/*
|
|
* Free the copy of the argument we made and return the number of options
|
|
* found.
|
|
*/
|
|
|
|
free(copyarg);
|
|
|
|
DEBUG_printf(("1cupsParseOptions: Returning %d", num_options));
|
|
|
|
return (num_options);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'cupsRemoveOption()' - Remove an option from an option array.
|
|
*
|
|
* @since CUPS 1.2/macOS 10.5@
|
|
*/
|
|
|
|
int /* O - New number of options */
|
|
cupsRemoveOption(
|
|
const char *name, /* I - Option name */
|
|
int num_options, /* I - Current number of options */
|
|
cups_option_t **options) /* IO - Options */
|
|
{
|
|
int i; /* Looping var */
|
|
cups_option_t *option; /* Current option */
|
|
|
|
|
|
DEBUG_printf(("2cupsRemoveOption(name=\"%s\", num_options=%d, options=%p)", name, num_options, (void *)options));
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (!name || num_options < 1 || !options)
|
|
{
|
|
DEBUG_printf(("3cupsRemoveOption: Returning %d", num_options));
|
|
return (num_options);
|
|
}
|
|
|
|
/*
|
|
* Loop for the option...
|
|
*/
|
|
|
|
for (i = num_options, option = *options; i > 0; i --, option ++)
|
|
if (!_cups_strcasecmp(name, option->name))
|
|
break;
|
|
|
|
if (i)
|
|
{
|
|
/*
|
|
* Remove this option from the array...
|
|
*/
|
|
|
|
DEBUG_puts("4cupsRemoveOption: Found option, removing it...");
|
|
|
|
num_options --;
|
|
i --;
|
|
|
|
_cupsStrFree(option->name);
|
|
_cupsStrFree(option->value);
|
|
|
|
if (i > 0)
|
|
memmove(option, option + 1, (size_t)i * sizeof(cups_option_t));
|
|
}
|
|
|
|
/*
|
|
* Return the new number of options...
|
|
*/
|
|
|
|
DEBUG_printf(("3cupsRemoveOption: Returning %d", num_options));
|
|
return (num_options);
|
|
}
|
|
|
|
|
|
/*
|
|
* '_cupsGet1284Values()' - Get 1284 device ID keys and values.
|
|
*
|
|
* The returned dictionary is a CUPS option array that can be queried with
|
|
* cupsGetOption and freed with cupsFreeOptions.
|
|
*/
|
|
|
|
int /* O - Number of key/value pairs */
|
|
_cupsGet1284Values(
|
|
const char *device_id, /* I - IEEE-1284 device ID string */
|
|
cups_option_t **values) /* O - Array of key/value pairs */
|
|
{
|
|
int num_values; /* Number of values */
|
|
char key[256], /* Key string */
|
|
value[256], /* Value string */
|
|
*ptr; /* Pointer into key/value */
|
|
|
|
|
|
/*
|
|
* Range check input...
|
|
*/
|
|
|
|
if (values)
|
|
*values = NULL;
|
|
|
|
if (!device_id || !values)
|
|
return (0);
|
|
|
|
/*
|
|
* Parse the 1284 device ID value into keys and values. The format is
|
|
* repeating sequences of:
|
|
*
|
|
* [whitespace]key:value[whitespace];
|
|
*/
|
|
|
|
num_values = 0;
|
|
while (*device_id)
|
|
{
|
|
while (_cups_isspace(*device_id))
|
|
device_id ++;
|
|
|
|
if (!*device_id)
|
|
break;
|
|
|
|
for (ptr = key; *device_id && *device_id != ':'; device_id ++)
|
|
if (ptr < (key + sizeof(key) - 1))
|
|
*ptr++ = *device_id;
|
|
|
|
if (!*device_id)
|
|
break;
|
|
|
|
while (ptr > key && _cups_isspace(ptr[-1]))
|
|
ptr --;
|
|
|
|
*ptr = '\0';
|
|
device_id ++;
|
|
|
|
while (_cups_isspace(*device_id))
|
|
device_id ++;
|
|
|
|
if (!*device_id)
|
|
break;
|
|
|
|
for (ptr = value; *device_id && *device_id != ';'; device_id ++)
|
|
if (ptr < (value + sizeof(value) - 1))
|
|
*ptr++ = *device_id;
|
|
|
|
if (!*device_id)
|
|
break;
|
|
|
|
while (ptr > value && _cups_isspace(ptr[-1]))
|
|
ptr --;
|
|
|
|
*ptr = '\0';
|
|
device_id ++;
|
|
|
|
num_values = cupsAddOption(key, value, num_values, values);
|
|
}
|
|
|
|
return (num_values);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'cups_compare_options()' - Compare two options.
|
|
*/
|
|
|
|
static int /* O - Result of comparison */
|
|
cups_compare_options(cups_option_t *a, /* I - First option */
|
|
cups_option_t *b) /* I - Second option */
|
|
{
|
|
return (_cups_strcasecmp(a->name, b->name));
|
|
}
|
|
|
|
|
|
/*
|
|
* 'cups_find_option()' - Find an option using a binary search.
|
|
*/
|
|
|
|
static int /* O - Index of match */
|
|
cups_find_option(
|
|
const char *name, /* I - Option name */
|
|
int num_options, /* I - Number of options */
|
|
cups_option_t *options, /* I - Options */
|
|
int prev, /* I - Previous index */
|
|
int *rdiff) /* O - Difference of match */
|
|
{
|
|
int left, /* Low mark for binary search */
|
|
right, /* High mark for binary search */
|
|
current, /* Current index */
|
|
diff; /* Result of comparison */
|
|
cups_option_t key; /* Search key */
|
|
|
|
|
|
DEBUG_printf(("7cups_find_option(name=\"%s\", num_options=%d, options=%p, prev=%d, rdiff=%p)", name, num_options, (void *)options, prev, (void *)rdiff));
|
|
|
|
#ifdef DEBUG
|
|
for (left = 0; left < num_options; left ++)
|
|
DEBUG_printf(("9cups_find_option: options[%d].name=\"%s\", .value=\"%s\"",
|
|
left, options[left].name, options[left].value));
|
|
#endif /* DEBUG */
|
|
|
|
key.name = (char *)name;
|
|
|
|
if (prev >= 0)
|
|
{
|
|
/*
|
|
* Start search on either side of previous...
|
|
*/
|
|
|
|
if ((diff = cups_compare_options(&key, options + prev)) == 0 ||
|
|
(diff < 0 && prev == 0) ||
|
|
(diff > 0 && prev == (num_options - 1)))
|
|
{
|
|
*rdiff = diff;
|
|
return (prev);
|
|
}
|
|
else if (diff < 0)
|
|
{
|
|
/*
|
|
* Start with previous on right side...
|
|
*/
|
|
|
|
left = 0;
|
|
right = prev;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Start wih previous on left side...
|
|
*/
|
|
|
|
left = prev;
|
|
right = num_options - 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Start search in the middle...
|
|
*/
|
|
|
|
left = 0;
|
|
right = num_options - 1;
|
|
}
|
|
|
|
do
|
|
{
|
|
current = (left + right) / 2;
|
|
diff = cups_compare_options(&key, options + current);
|
|
|
|
if (diff == 0)
|
|
break;
|
|
else if (diff < 0)
|
|
right = current;
|
|
else
|
|
left = current;
|
|
}
|
|
while ((right - left) > 1);
|
|
|
|
if (diff != 0)
|
|
{
|
|
/*
|
|
* Check the last 1 or 2 elements...
|
|
*/
|
|
|
|
if ((diff = cups_compare_options(&key, options + left)) <= 0)
|
|
current = left;
|
|
else
|
|
{
|
|
diff = cups_compare_options(&key, options + right);
|
|
current = right;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Return the closest destination and the difference...
|
|
*/
|
|
|
|
*rdiff = diff;
|
|
|
|
return (current);
|
|
}
|