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.

3467 lines
80 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

/*
* PPD file routines for CUPS.
*
* Copyright © 2007-2019 by Apple Inc.
* Copyright © 1997-2007 by Easy Software Products, all rights reserved.
*
* Licensed under Apache License v2.0. See the file "LICENSE" for more
* information.
*
* PostScript is a trademark of Adobe Systems, Inc.
*/
/*
* Include necessary headers.
*/
#include "cups-private.h"
#include "ppd-private.h"
#include "debug-internal.h"
/*
* Definitions...
*/
#define PPD_KEYWORD 1 /* Line contained a keyword */
#define PPD_OPTION 2 /* Line contained an option name */
#define PPD_TEXT 4 /* Line contained human-readable text */
#define PPD_STRING 8 /* Line contained a string or code */
#define PPD_HASHSIZE 512 /* Size of hash */
/*
* Line buffer structure...
*/
typedef struct _ppd_line_s
{
char *buffer; /* Pointer to buffer */
size_t bufsize; /* Size of the buffer */
} _ppd_line_t;
/*
* Local globals...
*/
static _cups_threadkey_t ppd_globals_key = _CUPS_THREADKEY_INITIALIZER;
/* Thread local storage key */
#ifdef HAVE_PTHREAD_H
static pthread_once_t ppd_globals_key_once = PTHREAD_ONCE_INIT;
/* One-time initialization object */
#endif /* HAVE_PTHREAD_H */
/*
* Local functions...
*/
static ppd_attr_t *ppd_add_attr(ppd_file_t *ppd, const char *name,
const char *spec, const char *text,
const char *value);
static ppd_choice_t *ppd_add_choice(ppd_option_t *option, const char *name);
static ppd_size_t *ppd_add_size(ppd_file_t *ppd, const char *name);
static int ppd_compare_attrs(ppd_attr_t *a, ppd_attr_t *b);
static int ppd_compare_choices(ppd_choice_t *a, ppd_choice_t *b);
static int ppd_compare_coptions(ppd_coption_t *a,
ppd_coption_t *b);
static int ppd_compare_options(ppd_option_t *a, ppd_option_t *b);
static int ppd_decode(char *string);
static void ppd_free_filters(ppd_file_t *ppd);
static void ppd_free_group(ppd_group_t *group);
static void ppd_free_option(ppd_option_t *option);
static ppd_coption_t *ppd_get_coption(ppd_file_t *ppd, const char *name);
static ppd_cparam_t *ppd_get_cparam(ppd_coption_t *opt,
const char *param,
const char *text);
static ppd_group_t *ppd_get_group(ppd_file_t *ppd, const char *name,
const char *text, _ppd_globals_t *pg,
cups_encoding_t encoding);
static ppd_option_t *ppd_get_option(ppd_group_t *group, const char *name);
static _ppd_globals_t *ppd_globals_alloc(void);
#if defined(HAVE_PTHREAD_H) || defined(_WIN32)
static void ppd_globals_free(_ppd_globals_t *g);
#endif /* HAVE_PTHREAD_H || _WIN32 */
#ifdef HAVE_PTHREAD_H
static void ppd_globals_init(void);
#endif /* HAVE_PTHREAD_H */
static int ppd_hash_option(ppd_option_t *option);
static int ppd_read(cups_file_t *fp, _ppd_line_t *line,
char *keyword, char *option, char *text,
char **string, int ignoreblank,
_ppd_globals_t *pg);
static int ppd_update_filters(ppd_file_t *ppd,
_ppd_globals_t *pg);
/*
* 'ppdClose()' - Free all memory used by the PPD file.
*/
void
ppdClose(ppd_file_t *ppd) /* I - PPD file record */
{
int i; /* Looping var */
ppd_group_t *group; /* Current group */
char **font; /* Current font */
ppd_attr_t **attr; /* Current attribute */
ppd_coption_t *coption; /* Current custom option */
ppd_cparam_t *cparam; /* Current custom parameter */
/*
* Range check arguments...
*/
if (!ppd)
return;
/*
* Free all strings at the top level...
*/
free(ppd->lang_encoding);
free(ppd->nickname);
free(ppd->patches);
free(ppd->jcl_begin);
free(ppd->jcl_end);
free(ppd->jcl_ps);
/*
* Free any UI groups, subgroups, and options...
*/
if (ppd->num_groups > 0)
{
for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
ppd_free_group(group);
free(ppd->groups);
}
cupsArrayDelete(ppd->options);
cupsArrayDelete(ppd->marked);
/*
* Free any page sizes...
*/
if (ppd->num_sizes > 0)
free(ppd->sizes);
/*
* Free any constraints...
*/
if (ppd->num_consts > 0)
free(ppd->consts);
/*
* Free any filters...
*/
ppd_free_filters(ppd);
/*
* Free any fonts...
*/
if (ppd->num_fonts > 0)
{
for (i = ppd->num_fonts, font = ppd->fonts; i > 0; i --, font ++)
free(*font);
free(ppd->fonts);
}
/*
* Free any profiles...
*/
if (ppd->num_profiles > 0)
free(ppd->profiles);
/*
* Free any attributes...
*/
if (ppd->num_attrs > 0)
{
for (i = ppd->num_attrs, attr = ppd->attrs; i > 0; i --, attr ++)
{
free((*attr)->value);
free(*attr);
}
free(ppd->attrs);
}
cupsArrayDelete(ppd->sorted_attrs);
/*
* Free custom options...
*/
for (coption = (ppd_coption_t *)cupsArrayFirst(ppd->coptions);
coption;
coption = (ppd_coption_t *)cupsArrayNext(ppd->coptions))
{
for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
cparam;
cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
{
switch (cparam->type)
{
case PPD_CUSTOM_PASSCODE :
case PPD_CUSTOM_PASSWORD :
case PPD_CUSTOM_STRING :
free(cparam->current.custom_string);
break;
default :
break;
}
free(cparam);
}
cupsArrayDelete(coption->params);
free(coption);
}
cupsArrayDelete(ppd->coptions);
/*
* Free constraints...
*/
if (ppd->cups_uiconstraints)
{
_ppd_cups_uiconsts_t *consts; /* Current constraints */
for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(ppd->cups_uiconstraints);
consts;
consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(ppd->cups_uiconstraints))
{
free(consts->constraints);
free(consts);
}
cupsArrayDelete(ppd->cups_uiconstraints);
}
/*
* Free any PPD cache/mapping data...
*/
if (ppd->cache)
_ppdCacheDestroy(ppd->cache);
/*
* Free the whole record...
*/
free(ppd);
}
/*
* 'ppdErrorString()' - Returns the text associated with a status.
*
* @since CUPS 1.1.19/macOS 10.3@
*/
const char * /* O - Status string */
ppdErrorString(ppd_status_t status) /* I - PPD status */
{
static const char * const messages[] =/* Status messages */
{
_("OK"),
_("Unable to open PPD file"),
_("NULL PPD file pointer"),
_("Memory allocation error"),
_("Missing PPD-Adobe-4.x header"),
_("Missing value string"),
_("Internal error"),
_("Bad OpenGroup"),
_("OpenGroup without a CloseGroup first"),
_("Bad OpenUI/JCLOpenUI"),
_("OpenUI/JCLOpenUI without a CloseUI/JCLCloseUI first"),
_("Bad OrderDependency"),
_("Bad UIConstraints"),
_("Missing asterisk in column 1"),
_("Line longer than the maximum allowed (255 characters)"),
_("Illegal control character"),
_("Illegal main keyword string"),
_("Illegal option keyword string"),
_("Illegal translation string"),
_("Illegal whitespace character"),
_("Bad custom parameter"),
_("Missing option keyword"),
_("Bad value string"),
_("Missing CloseGroup"),
_("Bad CloseUI/JCLCloseUI"),
_("Missing CloseUI/JCLCloseUI")
};
if (status < PPD_OK || status >= PPD_MAX_STATUS)
return (_cupsLangString(cupsLangDefault(), _("Unknown")));
else
return (_cupsLangString(cupsLangDefault(), messages[status]));
}
/*
* '_ppdGetEncoding()' - Get the CUPS encoding value for the given
* LanguageEncoding.
*/
cups_encoding_t /* O - CUPS encoding value */
_ppdGetEncoding(const char *name) /* I - LanguageEncoding string */
{
if (!_cups_strcasecmp(name, "ISOLatin1"))
return (CUPS_ISO8859_1);
else if (!_cups_strcasecmp(name, "ISOLatin2"))
return (CUPS_ISO8859_2);
else if (!_cups_strcasecmp(name, "ISOLatin5"))
return (CUPS_ISO8859_5);
else if (!_cups_strcasecmp(name, "JIS83-RKSJ"))
return (CUPS_JIS_X0213);
else if (!_cups_strcasecmp(name, "MacStandard"))
return (CUPS_MAC_ROMAN);
else if (!_cups_strcasecmp(name, "WindowsANSI"))
return (CUPS_WINDOWS_1252);
else
return (CUPS_UTF8);
}
/*
* '_ppdGlobals()' - Return a pointer to thread local storage
*/
_ppd_globals_t * /* O - Pointer to global data */
_ppdGlobals(void)
{
_ppd_globals_t *pg; /* Pointer to global data */
#ifdef HAVE_PTHREAD_H
/*
* Initialize the global data exactly once...
*/
pthread_once(&ppd_globals_key_once, ppd_globals_init);
#endif /* HAVE_PTHREAD_H */
/*
* See if we have allocated the data yet...
*/
if ((pg = (_ppd_globals_t *)_cupsThreadGetData(ppd_globals_key)) == NULL)
{
/*
* No, allocate memory as set the pointer for the key...
*/
if ((pg = ppd_globals_alloc()) != NULL)
_cupsThreadSetData(ppd_globals_key, pg);
}
/*
* Return the pointer to the data...
*/
return (pg);
}
/*
* 'ppdLastError()' - Return the status from the last ppdOpen*().
*
* @since CUPS 1.1.19/macOS 10.3@
*/
ppd_status_t /* O - Status code */
ppdLastError(int *line) /* O - Line number */
{
_ppd_globals_t *pg = _ppdGlobals();
/* Global data */
if (line)
*line = pg->ppd_line;
return (pg->ppd_status);
}
/*
* '_ppdOpen()' - Read a PPD file into memory.
*
* @since CUPS 1.2/macOS 10.5@
*/
ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
_ppdOpen(
cups_file_t *fp, /* I - File to read from */
_ppd_localization_t localization) /* I - Localization to load */
{
int i, j, k; /* Looping vars */
_ppd_line_t line; /* Line buffer */
ppd_file_t *ppd; /* PPD file record */
ppd_group_t *group, /* Current group */
*subgroup; /* Current sub-group */
ppd_option_t *option; /* Current option */
ppd_choice_t *choice; /* Current choice */
ppd_const_t *constraint; /* Current constraint */
ppd_size_t *size; /* Current page size */
int mask; /* Line data mask */
char keyword[PPD_MAX_NAME],
/* Keyword from file */
name[PPD_MAX_NAME],
/* Option from file */
text[PPD_MAX_LINE],
/* Human-readable text from file */
*string, /* Code/text from file */
*sptr, /* Pointer into string */
*temp, /* Temporary string pointer */
**tempfonts; /* Temporary fonts pointer */
float order; /* Order dependency number */
ppd_section_t section; /* Order dependency section */
ppd_profile_t *profile; /* Pointer to color profile */
char **filter; /* Pointer to filter */
struct lconv *loc; /* Locale data */
int ui_keyword; /* Is this line a UI keyword? */
cups_lang_t *lang; /* Language data */
cups_encoding_t encoding; /* Encoding of PPD file */
_ppd_globals_t *pg = _ppdGlobals();
/* Global data */
char custom_name[PPD_MAX_NAME];
/* CustomFoo attribute name */
ppd_attr_t *custom_attr; /* CustomFoo attribute */
char ll[7], /* Base language + '.' */
ll_CC[7]; /* Language w/country + '.' */
size_t ll_len = 0, /* Base language length */
ll_CC_len = 0; /* Language w/country length */
static const char * const ui_keywords[] =
{
#ifdef CUPS_USE_FULL_UI_KEYWORDS_LIST
/*
* Adobe defines some 41 keywords as "UI", meaning that they are
* user interface elements and that they should be treated as such
* even if the PPD creator doesn't use Open/CloseUI around them.
*
* Since this can cause previously invisible options to appear and
* confuse users, the default is to only treat the PageSize and
* PageRegion keywords this way.
*/
/* Boolean keywords */
"BlackSubstitution",
"Booklet",
"Collate",
"ManualFeed",
"MirrorPrint",
"NegativePrint",
"Sorter",
"TraySwitch",
/* PickOne keywords */
"AdvanceMedia",
"BindColor",
"BindEdge",
"BindType",
"BindWhen",
"BitsPerPixel",
"ColorModel",
"CutMedia",
"Duplex",
"FoldType",
"FoldWhen",
"InputSlot",
"JCLFrameBufferSize",
"JCLResolution",
"Jog",
"MediaColor",
"MediaType",
"MediaWeight",
"OutputBin",
"OutputMode",
"OutputOrder",
"PageRegion",
"PageSize",
"Resolution",
"Separations",
"Signature",
"Slipsheet",
"Smoothing",
"StapleLocation",
"StapleOrientation",
"StapleWhen",
"StapleX",
"StapleY"
#else /* !CUPS_USE_FULL_UI_KEYWORDS_LIST */
"PageRegion",
"PageSize"
#endif /* CUPS_USE_FULL_UI_KEYWORDS_LIST */
};
static const char * const color_keywords[] = /* Keywords associated with color profiles */
{
".cupsICCProfile",
".ColorModel",
};
DEBUG_printf(("_ppdOpen(fp=%p)", fp));
/*
* Default to "OK" status...
*/
pg->ppd_status = PPD_OK;
pg->ppd_line = 0;
/*
* Range check input...
*/
if (fp == NULL)
{
pg->ppd_status = PPD_NULL_FILE;
return (NULL);
}
/*
* If only loading a single localization set up the strings to match...
*/
if (localization == _PPD_LOCALIZATION_DEFAULT)
{
if ((lang = cupsLangDefault()) == NULL)
return (NULL);
snprintf(ll_CC, sizeof(ll_CC), "%s.", lang->language);
/*
* <rdar://problem/22130168>
* <rdar://problem/27245567>
*
* Need to use a different base language for some locales...
*/
if (!strcmp(lang->language, "zh_HK"))
{ /* Traditional Chinese + variants */
strlcpy(ll_CC, "zh_TW.", sizeof(ll_CC));
strlcpy(ll, "zh_", sizeof(ll));
}
else if (!strncmp(lang->language, "zh", 2))
strlcpy(ll, "zh_", sizeof(ll)); /* Any Chinese variant */
else if (!strncmp(lang->language, "jp", 2))
{ /* Any Japanese variant */
strlcpy(ll_CC, "ja", sizeof(ll_CC));
strlcpy(ll, "jp", sizeof(ll));
}
else if (!strncmp(lang->language, "nb", 2) || !strncmp(lang->language, "no", 2))
{ /* Any Norwegian variant */
strlcpy(ll_CC, "nb", sizeof(ll_CC));
strlcpy(ll, "no", sizeof(ll));
}
else
snprintf(ll, sizeof(ll), "%2.2s.", lang->language);
ll_CC_len = strlen(ll_CC);
ll_len = strlen(ll);
DEBUG_printf(("2_ppdOpen: Loading localizations matching \"%s\" and \"%s\"",
ll_CC, ll));
}
/*
* Grab the first line and make sure it reads '*PPD-Adobe: "major.minor"'...
*/
line.buffer = NULL;
line.bufsize = 0;
mask = ppd_read(fp, &line, keyword, name, text, &string, 0, pg);
DEBUG_printf(("2_ppdOpen: mask=%x, keyword=\"%s\"...", mask, keyword));
if (mask == 0 ||
strcmp(keyword, "PPD-Adobe") ||
string == NULL || string[0] != '4')
{
/*
* Either this is not a PPD file, or it is not a 4.x PPD file.
*/
if (pg->ppd_status == PPD_OK)
pg->ppd_status = PPD_MISSING_PPDADOBE4;
free(string);
free(line.buffer);
return (NULL);
}
DEBUG_printf(("2_ppdOpen: keyword=%s, string=%p", keyword, string));
/*
* Allocate memory for the PPD file record...
*/
if ((ppd = calloc(1, sizeof(ppd_file_t))) == NULL)
{
pg->ppd_status = PPD_ALLOC_ERROR;
free(string);
free(line.buffer);
return (NULL);
}
free(string);
string = NULL;
ppd->language_level = 2;
ppd->color_device = 0;
ppd->colorspace = PPD_CS_N;
ppd->landscape = -90;
ppd->coptions = cupsArrayNew((cups_array_func_t)ppd_compare_coptions, NULL);
/*
* Read lines from the PPD file and add them to the file record...
*/
group = NULL;
subgroup = NULL;
option = NULL;
choice = NULL;
ui_keyword = 0;
encoding = CUPS_ISO8859_1;
loc = localeconv();
while ((mask = ppd_read(fp, &line, keyword, name, text, &string, 1, pg)) != 0)
{
DEBUG_printf(("2_ppdOpen: mask=%x, keyword=\"%s\", name=\"%s\", "
"text=\"%s\", string=%d chars...", mask, keyword, name, text,
string ? (int)strlen(string) : 0));
if (strncmp(keyword, "Default", 7) && !string &&
pg->ppd_conform != PPD_CONFORM_RELAXED)
{
/*
* Need a string value!
*/
pg->ppd_status = PPD_MISSING_VALUE;
goto error;
}
else if (!string)
continue;
/*
* Certain main keywords (as defined by the PPD spec) may be used
* without the usual OpenUI/CloseUI stuff. Presumably this is just
* so that Adobe wouldn't completely break compatibility with PPD
* files prior to v4.0 of the spec, but it is hopelessly
* inconsistent... Catch these main keywords and automatically
* create the corresponding option, as needed...
*/
if (ui_keyword)
{
/*
* Previous line was a UI keyword...
*/
option = NULL;
ui_keyword = 0;
}
/*
* If we are filtering out keyword localizations, see if this line needs to
* be used...
*/
if (localization != _PPD_LOCALIZATION_ALL &&
(temp = strchr(keyword, '.')) != NULL &&
((temp - keyword) == 2 || (temp - keyword) == 5) &&
_cups_isalpha(keyword[0]) &&
_cups_isalpha(keyword[1]) &&
(keyword[2] == '.' ||
(keyword[2] == '_' && _cups_isalpha(keyword[3]) &&
_cups_isalpha(keyword[4]) && keyword[5] == '.')))
{
if (localization == _PPD_LOCALIZATION_NONE ||
(localization == _PPD_LOCALIZATION_DEFAULT &&
strncmp(ll_CC, keyword, ll_CC_len) &&
strncmp(ll, keyword, ll_len)))
{
DEBUG_printf(("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword));
free(string);
string = NULL;
continue;
}
else if (localization == _PPD_LOCALIZATION_ICC_PROFILES)
{
/*
* Only load localizations for the color profile related keywords...
*/
for (i = 0;
i < (int)(sizeof(color_keywords) / sizeof(color_keywords[0]));
i ++)
{
if (!_cups_strcasecmp(temp, color_keywords[i]))
break;
}
if (i >= (int)(sizeof(color_keywords) / sizeof(color_keywords[0])))
{
DEBUG_printf(("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword));
free(string);
string = NULL;
continue;
}
}
}
if (option == NULL &&
(mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) ==
(PPD_KEYWORD | PPD_OPTION | PPD_STRING))
{
for (i = 0; i < (int)(sizeof(ui_keywords) / sizeof(ui_keywords[0])); i ++)
if (!strcmp(keyword, ui_keywords[i]))
break;
if (i < (int)(sizeof(ui_keywords) / sizeof(ui_keywords[0])))
{
/*
* Create the option in the appropriate group...
*/
ui_keyword = 1;
DEBUG_printf(("2_ppdOpen: FOUND ADOBE UI KEYWORD %s WITHOUT OPENUI!",
keyword));
if (!group)
{
if ((group = ppd_get_group(ppd, "General", _("General"), pg,
encoding)) == NULL)
goto error;
DEBUG_printf(("2_ppdOpen: Adding to group %s...", group->text));
option = ppd_get_option(group, keyword);
group = NULL;
}
else
option = ppd_get_option(group, keyword);
if (option == NULL)
{
pg->ppd_status = PPD_ALLOC_ERROR;
goto error;
}
/*
* Now fill in the initial information for the option...
*/
if (!strncmp(keyword, "JCL", 3))
option->section = PPD_ORDER_JCL;
else
option->section = PPD_ORDER_ANY;
option->order = 10.0f;
if (i < 8)
option->ui = PPD_UI_BOOLEAN;
else
option->ui = PPD_UI_PICKONE;
for (j = 0; j < ppd->num_attrs; j ++)
if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
!strcmp(ppd->attrs[j]->name + 7, keyword) &&
ppd->attrs[j]->value)
{
DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
option->keyword, ppd->attrs[j]->value));
strlcpy(option->defchoice, ppd->attrs[j]->value,
sizeof(option->defchoice));
break;
}
if (!strcmp(keyword, "PageSize"))
strlcpy(option->text, _("Media Size"), sizeof(option->text));
else if (!strcmp(keyword, "MediaType"))
strlcpy(option->text, _("Media Type"), sizeof(option->text));
else if (!strcmp(keyword, "InputSlot"))
strlcpy(option->text, _("Media Source"), sizeof(option->text));
else if (!strcmp(keyword, "ColorModel"))
strlcpy(option->text, _("Output Mode"), sizeof(option->text));
else if (!strcmp(keyword, "Resolution"))
strlcpy(option->text, _("Resolution"), sizeof(option->text));
else
strlcpy(option->text, keyword, sizeof(option->text));
}
}
if (!strcmp(keyword, "LanguageLevel"))
ppd->language_level = atoi(string);
else if (!strcmp(keyword, "LanguageEncoding"))
{
/*
* Say all PPD files are UTF-8, since we convert to UTF-8...
*/
ppd->lang_encoding = strdup("UTF-8");
encoding = _ppdGetEncoding(string);
}
else if (!strcmp(keyword, "LanguageVersion"))
ppd->lang_version = string;
else if (!strcmp(keyword, "Manufacturer"))
ppd->manufacturer = string;
else if (!strcmp(keyword, "ModelName"))
ppd->modelname = string;
else if (!strcmp(keyword, "Protocols"))
ppd->protocols = string;
else if (!strcmp(keyword, "PCFileName"))
ppd->pcfilename = string;
else if (!strcmp(keyword, "NickName"))
{
if (encoding != CUPS_UTF8)
{
cups_utf8_t utf8[256]; /* UTF-8 version of NickName */
cupsCharsetToUTF8(utf8, string, sizeof(utf8), encoding);
ppd->nickname = strdup((char *)utf8);
}
else
ppd->nickname = strdup(string);
}
else if (!strcmp(keyword, "Product"))
ppd->product = string;
else if (!strcmp(keyword, "ShortNickName"))
ppd->shortnickname = string;
else if (!strcmp(keyword, "TTRasterizer"))
ppd->ttrasterizer = string;
else if (!strcmp(keyword, "JCLBegin"))
{
ppd->jcl_begin = strdup(string);
ppd_decode(ppd->jcl_begin); /* Decode quoted string */
}
else if (!strcmp(keyword, "JCLEnd"))
{
ppd->jcl_end = strdup(string);
ppd_decode(ppd->jcl_end); /* Decode quoted string */
}
else if (!strcmp(keyword, "JCLToPSInterpreter"))
{
ppd->jcl_ps = strdup(string);
ppd_decode(ppd->jcl_ps); /* Decode quoted string */
}
else if (!strcmp(keyword, "AccurateScreensSupport"))
ppd->accurate_screens = !strcmp(string, "True");
else if (!strcmp(keyword, "ColorDevice"))
ppd->color_device = !strcmp(string, "True");
else if (!strcmp(keyword, "ContoneOnly"))
ppd->contone_only = !strcmp(string, "True");
else if (!strcmp(keyword, "cupsFlipDuplex"))
ppd->flip_duplex = !strcmp(string, "True");
else if (!strcmp(keyword, "cupsManualCopies"))
ppd->manual_copies = !strcmp(string, "True");
else if (!strcmp(keyword, "cupsModelNumber"))
ppd->model_number = atoi(string);
else if (!strcmp(keyword, "cupsColorProfile"))
{
if (ppd->num_profiles == 0)
profile = malloc(sizeof(ppd_profile_t));
else
profile = realloc(ppd->profiles, sizeof(ppd_profile_t) * (size_t)(ppd->num_profiles + 1));
if (!profile)
{
pg->ppd_status = PPD_ALLOC_ERROR;
goto error;
}
ppd->profiles = profile;
profile += ppd->num_profiles;
ppd->num_profiles ++;
memset(profile, 0, sizeof(ppd_profile_t));
strlcpy(profile->resolution, name, sizeof(profile->resolution));
strlcpy(profile->media_type, text, sizeof(profile->media_type));
profile->density = (float)_cupsStrScand(string, &sptr, loc);
profile->gamma = (float)_cupsStrScand(sptr, &sptr, loc);
profile->matrix[0][0] = (float)_cupsStrScand(sptr, &sptr, loc);
profile->matrix[0][1] = (float)_cupsStrScand(sptr, &sptr, loc);
profile->matrix[0][2] = (float)_cupsStrScand(sptr, &sptr, loc);
profile->matrix[1][0] = (float)_cupsStrScand(sptr, &sptr, loc);
profile->matrix[1][1] = (float)_cupsStrScand(sptr, &sptr, loc);
profile->matrix[1][2] = (float)_cupsStrScand(sptr, &sptr, loc);
profile->matrix[2][0] = (float)_cupsStrScand(sptr, &sptr, loc);
profile->matrix[2][1] = (float)_cupsStrScand(sptr, &sptr, loc);
profile->matrix[2][2] = (float)_cupsStrScand(sptr, &sptr, loc);
}
else if (!strcmp(keyword, "cupsFilter"))
{
if (ppd->num_filters == 0)
filter = malloc(sizeof(char *));
else
filter = realloc(ppd->filters, sizeof(char *) * (size_t)(ppd->num_filters + 1));
if (filter == NULL)
{
pg->ppd_status = PPD_ALLOC_ERROR;
goto error;
}
ppd->filters = filter;
filter += ppd->num_filters;
ppd->num_filters ++;
/*
* Make a copy of the filter string...
*/
*filter = strdup(string);
}
else if (!strcmp(keyword, "Throughput"))
ppd->throughput = atoi(string);
else if (!strcmp(keyword, "Font"))
{
/*
* Add this font to the list of available fonts...
*/
if (ppd->num_fonts == 0)
tempfonts = (char **)malloc(sizeof(char *));
else
tempfonts = (char **)realloc(ppd->fonts, sizeof(char *) * (size_t)(ppd->num_fonts + 1));
if (tempfonts == NULL)
{
pg->ppd_status = PPD_ALLOC_ERROR;
goto error;
}
ppd->fonts = tempfonts;
ppd->fonts[ppd->num_fonts] = strdup(name);
ppd->num_fonts ++;
}
else if (!strncmp(keyword, "ParamCustom", 11))
{
ppd_coption_t *coption; /* Custom option */
ppd_cparam_t *cparam; /* Custom parameter */
int corder; /* Order number */
char ctype[33], /* Data type */
cminimum[65], /* Minimum value */
cmaximum[65]; /* Maximum value */
/*
* Get the custom option and parameter...
*/
if ((coption = ppd_get_coption(ppd, keyword + 11)) == NULL)
{
pg->ppd_status = PPD_ALLOC_ERROR;
goto error;
}
if ((cparam = ppd_get_cparam(coption, name, text)) == NULL)
{
pg->ppd_status = PPD_ALLOC_ERROR;
goto error;
}
if (cparam->type != PPD_CUSTOM_UNKNOWN)
{
pg->ppd_status = PPD_BAD_CUSTOM_PARAM;
goto error;
}
/*
* Get the parameter data...
*/
if (!string ||
sscanf(string, "%d%32s%64s%64s", &corder, ctype, cminimum,
cmaximum) != 4)
{
pg->ppd_status = PPD_BAD_CUSTOM_PARAM;
goto error;
}
cparam->order = corder;
if (!strcmp(ctype, "curve"))
{
cparam->type = PPD_CUSTOM_CURVE;
cparam->minimum.custom_curve = (float)_cupsStrScand(cminimum, NULL, loc);
cparam->maximum.custom_curve = (float)_cupsStrScand(cmaximum, NULL, loc);
}
else if (!strcmp(ctype, "int"))
{
cparam->type = PPD_CUSTOM_INT;
cparam->minimum.custom_int = atoi(cminimum);
cparam->maximum.custom_int = atoi(cmaximum);
}
else if (!strcmp(ctype, "invcurve"))
{
cparam->type = PPD_CUSTOM_INVCURVE;
cparam->minimum.custom_invcurve = (float)_cupsStrScand(cminimum, NULL, loc);
cparam->maximum.custom_invcurve = (float)_cupsStrScand(cmaximum, NULL, loc);
}
else if (!strcmp(ctype, "passcode"))
{
cparam->type = PPD_CUSTOM_PASSCODE;
cparam->minimum.custom_passcode = atoi(cminimum);
cparam->maximum.custom_passcode = atoi(cmaximum);
}
else if (!strcmp(ctype, "password"))
{
cparam->type = PPD_CUSTOM_PASSWORD;
cparam->minimum.custom_password = atoi(cminimum);
cparam->maximum.custom_password = atoi(cmaximum);
}
else if (!strcmp(ctype, "points"))
{
cparam->type = PPD_CUSTOM_POINTS;
cparam->minimum.custom_points = (float)_cupsStrScand(cminimum, NULL, loc);
cparam->maximum.custom_points = (float)_cupsStrScand(cmaximum, NULL, loc);
}
else if (!strcmp(ctype, "real"))
{
cparam->type = PPD_CUSTOM_REAL;
cparam->minimum.custom_real = (float)_cupsStrScand(cminimum, NULL, loc);
cparam->maximum.custom_real = (float)_cupsStrScand(cmaximum, NULL, loc);
}
else if (!strcmp(ctype, "string"))
{
cparam->type = PPD_CUSTOM_STRING;
cparam->minimum.custom_string = atoi(cminimum);
cparam->maximum.custom_string = atoi(cmaximum);
}
else
{
pg->ppd_status = PPD_BAD_CUSTOM_PARAM;
goto error;
}
/*
* Now special-case for CustomPageSize...
*/
if (!strcmp(coption->keyword, "PageSize"))
{
if (!strcmp(name, "Width"))
{
ppd->custom_min[0] = cparam->minimum.custom_points;
ppd->custom_max[0] = cparam->maximum.custom_points;
}
else if (!strcmp(name, "Height"))
{
ppd->custom_min[1] = cparam->minimum.custom_points;
ppd->custom_max[1] = cparam->maximum.custom_points;
}
}
}
else if (!strcmp(keyword, "HWMargins"))
{
for (i = 0, sptr = string; i < 4; i ++)
ppd->custom_margins[i] = (float)_cupsStrScand(sptr, &sptr, loc);
}
else if (!strncmp(keyword, "Custom", 6) && !strcmp(name, "True") && !option)
{
ppd_option_t *custom_option; /* Custom option */
DEBUG_puts("2_ppdOpen: Processing Custom option...");
/*
* Get the option and custom option...
*/
if (!ppd_get_coption(ppd, keyword + 6))
{
pg->ppd_status = PPD_ALLOC_ERROR;
goto error;
}
if (option && !_cups_strcasecmp(option->keyword, keyword + 6))
custom_option = option;
else
custom_option = ppdFindOption(ppd, keyword + 6);
if (custom_option)
{
/*
* Add the "custom" option...
*/
if ((choice = ppdFindChoice(custom_option, "Custom")) == NULL)
if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL)
{
DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
pg->ppd_status = PPD_ALLOC_ERROR;
goto error;
}
strlcpy(choice->text, text[0] ? text : _("Custom"),
sizeof(choice->text));
choice->code = strdup(string);
if (custom_option->section == PPD_ORDER_JCL)
ppd_decode(choice->code);
}
/*
* Now process custom page sizes specially...
*/
if (!strcmp(keyword, "CustomPageSize"))
{
/*
* Add a "Custom" page size entry...
*/
ppd->variable_sizes = 1;
ppd_add_size(ppd, "Custom");
if (option && !_cups_strcasecmp(option->keyword, "PageRegion"))
custom_option = option;
else
custom_option = ppdFindOption(ppd, "PageRegion");
if (custom_option)
{
if ((choice = ppdFindChoice(custom_option, "Custom")) == NULL)
if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL)
{
DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
pg->ppd_status = PPD_ALLOC_ERROR;
goto error;
}
strlcpy(choice->text, text[0] ? text : _("Custom"),
sizeof(choice->text));
}
}
}
else if (!strcmp(keyword, "LandscapeOrientation"))
{
if (!strcmp(string, "Minus90"))
ppd->landscape = -90;
else if (!strcmp(string, "Plus90"))
ppd->landscape = 90;
}
else if (!strcmp(keyword, "Emulators") && string && ppd->num_emulations == 0)
{
/*
* Issue #5562: Samsung printer drivers incorrectly use Emulators keyword
* to configure themselves
*
* The Emulators keyword was loaded but never used by anything in CUPS,
* and has no valid purpose in CUPS. The old code was removed due to a
* memory leak (Issue #5475), so the following (new) code supports a single
* name for the Emulators keyword, allowing these drivers to work until we
* remove PPD and driver support entirely in a future version of CUPS.
*/
ppd->num_emulations = 1;
ppd->emulations = calloc(1, sizeof(ppd_emul_t));
strlcpy(ppd->emulations[0].name, string, sizeof(ppd->emulations[0].name));
}
else if (!strcmp(keyword, "JobPatchFile"))
{
/*
* CUPS STR #3421: Check for "*JobPatchFile: int: string"
*/
if (isdigit(*string & 255))
{
for (sptr = string + 1; isdigit(*sptr & 255); sptr ++);
if (*sptr == ':')
{
/*
* Found "*JobPatchFile: int: string"...
*/
pg->ppd_status = PPD_BAD_VALUE;
goto error;
}
}
if (!name[0] && pg->ppd_conform == PPD_CONFORM_STRICT)
{
/*
* Found "*JobPatchFile: string"...
*/
pg->ppd_status = PPD_MISSING_OPTION_KEYWORD;
goto error;
}
if (ppd->patches == NULL)
ppd->patches = strdup(string);
else
{
temp = realloc(ppd->patches, strlen(ppd->patches) +
strlen(string) + 1);
if (temp == NULL)
{
pg->ppd_status = PPD_ALLOC_ERROR;
goto error;
}
ppd->patches = temp;
memcpy(ppd->patches + strlen(ppd->patches), string, strlen(string) + 1);
}
}
else if (!strcmp(keyword, "OpenUI"))
{
/*
* Don't allow nesting of options...
*/
if (option && pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_status = PPD_NESTED_OPEN_UI;
goto error;
}
/*
* Add an option record to the current sub-group, group, or file...
*/
DEBUG_printf(("2_ppdOpen: name=\"%s\" (%d)", name, (int)strlen(name)));
if (name[0] == '*')
_cups_strcpy(name, name + 1); /* Eliminate leading asterisk */
for (i = (int)strlen(name) - 1; i > 0 && _cups_isspace(name[i]); i --)
name[i] = '\0'; /* Eliminate trailing spaces */
DEBUG_printf(("2_ppdOpen: OpenUI of %s in group %s...", name,
group ? group->text : "(null)"));
if (subgroup != NULL)
option = ppd_get_option(subgroup, name);
else if (group == NULL)
{
if ((group = ppd_get_group(ppd, "General", _("General"), pg,
encoding)) == NULL)
goto error;
DEBUG_printf(("2_ppdOpen: Adding to group %s...", group->text));
option = ppd_get_option(group, name);
group = NULL;
}
else
option = ppd_get_option(group, name);
if (option == NULL)
{
pg->ppd_status = PPD_ALLOC_ERROR;
goto error;
}
/*
* Now fill in the initial information for the option...
*/
if (string && !strcmp(string, "PickMany"))
option->ui = PPD_UI_PICKMANY;
else if (string && !strcmp(string, "Boolean"))
option->ui = PPD_UI_BOOLEAN;
else if (string && !strcmp(string, "PickOne"))
option->ui = PPD_UI_PICKONE;
else if (pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_status = PPD_BAD_OPEN_UI;
goto error;
}
else
option->ui = PPD_UI_PICKONE;
for (j = 0; j < ppd->num_attrs; j ++)
if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
!strcmp(ppd->attrs[j]->name + 7, name) &&
ppd->attrs[j]->value)
{
DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
option->keyword, ppd->attrs[j]->value));
strlcpy(option->defchoice, ppd->attrs[j]->value,
sizeof(option->defchoice));
break;
}
if (text[0])
cupsCharsetToUTF8((cups_utf8_t *)option->text, text,
sizeof(option->text), encoding);
else
{
if (!strcmp(name, "PageSize"))
strlcpy(option->text, _("Media Size"), sizeof(option->text));
else if (!strcmp(name, "MediaType"))
strlcpy(option->text, _("Media Type"), sizeof(option->text));
else if (!strcmp(name, "InputSlot"))
strlcpy(option->text, _("Media Source"), sizeof(option->text));
else if (!strcmp(name, "ColorModel"))
strlcpy(option->text, _("Output Mode"), sizeof(option->text));
else if (!strcmp(name, "Resolution"))
strlcpy(option->text, _("Resolution"), sizeof(option->text));
else
strlcpy(option->text, name, sizeof(option->text));
}
option->section = PPD_ORDER_ANY;
free(string);
string = NULL;
/*
* Add a custom option choice if we have already seen a CustomFoo
* attribute...
*/
if (!_cups_strcasecmp(name, "PageRegion"))
strlcpy(custom_name, "CustomPageSize", sizeof(custom_name));
else
snprintf(custom_name, sizeof(custom_name), "Custom%s", name);
if ((custom_attr = ppdFindAttr(ppd, custom_name, "True")) != NULL)
{
if ((choice = ppdFindChoice(option, "Custom")) == NULL)
if ((choice = ppd_add_choice(option, "Custom")) == NULL)
{
DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
pg->ppd_status = PPD_ALLOC_ERROR;
goto error;
}
strlcpy(choice->text,
custom_attr->text[0] ? custom_attr->text : _("Custom"),
sizeof(choice->text));
choice->code = strdup(custom_attr->value);
}
}
else if (!strcmp(keyword, "JCLOpenUI"))
{
/*
* Don't allow nesting of options...
*/
if (option && pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_status = PPD_NESTED_OPEN_UI;
goto error;
}
/*
* Find the JCL group, and add if needed...
*/
group = ppd_get_group(ppd, "JCL", _("JCL"), pg, encoding);
if (group == NULL)
goto error;
/*
* Add an option record to the current JCLs...
*/
if (name[0] == '*')
_cups_strcpy(name, name + 1);
option = ppd_get_option(group, name);
if (option == NULL)
{
pg->ppd_status = PPD_ALLOC_ERROR;
goto error;
}
/*
* Now fill in the initial information for the option...
*/
if (string && !strcmp(string, "PickMany"))
option->ui = PPD_UI_PICKMANY;
else if (string && !strcmp(string, "Boolean"))
option->ui = PPD_UI_BOOLEAN;
else if (string && !strcmp(string, "PickOne"))
option->ui = PPD_UI_PICKONE;
else
{
pg->ppd_status = PPD_BAD_OPEN_UI;
goto error;
}
for (j = 0; j < ppd->num_attrs; j ++)
if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
!strcmp(ppd->attrs[j]->name + 7, name) &&
ppd->attrs[j]->value)
{
DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
option->keyword, ppd->attrs[j]->value));
strlcpy(option->defchoice, ppd->attrs[j]->value,
sizeof(option->defchoice));
break;
}
if (text[0])
cupsCharsetToUTF8((cups_utf8_t *)option->text, text,
sizeof(option->text), encoding);
else
strlcpy(option->text, name, sizeof(option->text));
option->section = PPD_ORDER_JCL;
group = NULL;
free(string);
string = NULL;
/*
* Add a custom option choice if we have already seen a CustomFoo
* attribute...
*/
snprintf(custom_name, sizeof(custom_name), "Custom%s", name);
if ((custom_attr = ppdFindAttr(ppd, custom_name, "True")) != NULL)
{
if ((choice = ppd_add_choice(option, "Custom")) == NULL)
{
DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
pg->ppd_status = PPD_ALLOC_ERROR;
goto error;
}
strlcpy(choice->text,
custom_attr->text[0] ? custom_attr->text : _("Custom"),
sizeof(choice->text));
choice->code = strdup(custom_attr->value);
}
}
else if (!strcmp(keyword, "CloseUI"))
{
if ((!option || option->section == PPD_ORDER_JCL) && pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_status = PPD_BAD_CLOSE_UI;
goto error;
}
option = NULL;
free(string);
string = NULL;
}
else if (!strcmp(keyword, "JCLCloseUI"))
{
if ((!option || option->section != PPD_ORDER_JCL) && pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_status = PPD_BAD_CLOSE_UI;
goto error;
}
option = NULL;
free(string);
string = NULL;
}
else if (!strcmp(keyword, "OpenGroup"))
{
/*
* Open a new group...
*/
if (group != NULL)
{
pg->ppd_status = PPD_NESTED_OPEN_GROUP;
goto error;
}
if (!string)
{
pg->ppd_status = PPD_BAD_OPEN_GROUP;
goto error;
}
/*
* Separate the group name from the text (name/text)...
*/
if ((sptr = strchr(string, '/')) != NULL)
*sptr++ = '\0';
else
sptr = string;
/*
* Fix up the text...
*/
ppd_decode(sptr);
/*
* Find/add the group...
*/
group = ppd_get_group(ppd, string, sptr, pg, encoding);
if (group == NULL)
goto error;
free(string);
string = NULL;
}
else if (!strcmp(keyword, "CloseGroup"))
{
group = NULL;
free(string);
string = NULL;
}
else if (!strcmp(keyword, "OrderDependency"))
{
order = (float)_cupsStrScand(string, &sptr, loc);
if (!sptr || sscanf(sptr, "%40s%40s", name, keyword) != 2)
{
pg->ppd_status = PPD_BAD_ORDER_DEPENDENCY;
goto error;
}
if (keyword[0] == '*')
_cups_strcpy(keyword, keyword + 1);
if (!strcmp(name, "ExitServer"))
section = PPD_ORDER_EXIT;
else if (!strcmp(name, "Prolog"))
section = PPD_ORDER_PROLOG;
else if (!strcmp(name, "DocumentSetup"))
section = PPD_ORDER_DOCUMENT;
else if (!strcmp(name, "PageSetup"))
section = PPD_ORDER_PAGE;
else if (!strcmp(name, "JCLSetup"))
section = PPD_ORDER_JCL;
else
section = PPD_ORDER_ANY;
if (option == NULL)
{
ppd_group_t *gtemp;
/*
* Only valid for Non-UI options...
*/
for (i = ppd->num_groups, gtemp = ppd->groups; i > 0; i --, gtemp ++)
if (gtemp->text[0] == '\0')
break;
if (i > 0)
for (i = 0; i < gtemp->num_options; i ++)
if (!strcmp(keyword, gtemp->options[i].keyword))
{
gtemp->options[i].section = section;
gtemp->options[i].order = order;
break;
}
}
else
{
option->section = section;
option->order = order;
}
free(string);
string = NULL;
}
else if (!strncmp(keyword, "Default", 7))
{
if (string == NULL)
continue;
/*
* Drop UI text, if any, from value...
*/
if (strchr(string, '/') != NULL)
*strchr(string, '/') = '\0';
/*
* Assign the default value as appropriate...
*/
if (!strcmp(keyword, "DefaultColorSpace"))
{
/*
* Set default colorspace...
*/
if (!strcmp(string, "CMY"))
ppd->colorspace = PPD_CS_CMY;
else if (!strcmp(string, "CMYK"))
ppd->colorspace = PPD_CS_CMYK;
else if (!strcmp(string, "RGB"))
ppd->colorspace = PPD_CS_RGB;
else if (!strcmp(string, "RGBK"))
ppd->colorspace = PPD_CS_RGBK;
else if (!strcmp(string, "N"))
ppd->colorspace = PPD_CS_N;
else
ppd->colorspace = PPD_CS_GRAY;
}
else if (option && !strcmp(keyword + 7, option->keyword))
{
/*
* Set the default as part of the current option...
*/
DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword, string));
strlcpy(option->defchoice, string, sizeof(option->defchoice));
DEBUG_printf(("2_ppdOpen: %s is now %s...", keyword, option->defchoice));
}
else
{
/*
* Lookup option and set if it has been defined...
*/
ppd_option_t *toption; /* Temporary option */
if ((toption = ppdFindOption(ppd, keyword + 7)) != NULL)
{
DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword, string));
strlcpy(toption->defchoice, string, sizeof(toption->defchoice));
}
}
}
else if (!strcmp(keyword, "UIConstraints") ||
!strcmp(keyword, "NonUIConstraints"))
{
if (!string)
{
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
goto error;
}
if (ppd->num_consts == 0)
constraint = calloc(2, sizeof(ppd_const_t));
else
constraint = realloc(ppd->consts, (size_t)(ppd->num_consts + 2) * sizeof(ppd_const_t));
if (constraint == NULL)
{
pg->ppd_status = PPD_ALLOC_ERROR;
goto error;
}
ppd->consts = constraint;
constraint += ppd->num_consts;
ppd->num_consts ++;
switch (sscanf(string, "%40s%40s%40s%40s", constraint->option1,
constraint->choice1, constraint->option2,
constraint->choice2))
{
default : /* Error */
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
goto error;
case 2 : /* Two options... */
/*
* Check for broken constraints like "* Option"...
*/
if (pg->ppd_conform == PPD_CONFORM_STRICT &&
(!strcmp(constraint->option1, "*") ||
!strcmp(constraint->choice1, "*")))
{
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
goto error;
}
/*
* The following strcpy's are safe, as optionN and
* choiceN are all the same size (size defined by PPD spec...)
*/
if (constraint->option1[0] == '*')
_cups_strcpy(constraint->option1, constraint->option1 + 1);
else if (pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
goto error;
}
if (constraint->choice1[0] == '*')
_cups_strcpy(constraint->option2, constraint->choice1 + 1);
else if (pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
goto error;
}
constraint->choice1[0] = '\0';
constraint->choice2[0] = '\0';
break;
case 3 : /* Two options, one choice... */
/*
* Check for broken constraints like "* Option"...
*/
if (pg->ppd_conform == PPD_CONFORM_STRICT &&
(!strcmp(constraint->option1, "*") ||
!strcmp(constraint->choice1, "*") ||
!strcmp(constraint->option2, "*")))
{
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
goto error;
}
/*
* The following _cups_strcpy's are safe, as optionN and
* choiceN are all the same size (size defined by PPD spec...)
*/
if (constraint->option1[0] == '*')
_cups_strcpy(constraint->option1, constraint->option1 + 1);
else if (pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
goto error;
}
if (constraint->choice1[0] == '*')
{
if (pg->ppd_conform == PPD_CONFORM_STRICT &&
constraint->option2[0] == '*')
{
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
goto error;
}
_cups_strcpy(constraint->choice2, constraint->option2);
_cups_strcpy(constraint->option2, constraint->choice1 + 1);
constraint->choice1[0] = '\0';
}
else
{
if (constraint->option2[0] == '*')
_cups_strcpy(constraint->option2, constraint->option2 + 1);
else if (pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
goto error;
}
constraint->choice2[0] = '\0';
}
break;
case 4 : /* Two options, two choices... */
/*
* Check for broken constraints like "* Option"...
*/
if (pg->ppd_conform == PPD_CONFORM_STRICT &&
(!strcmp(constraint->option1, "*") ||
!strcmp(constraint->choice1, "*") ||
!strcmp(constraint->option2, "*") ||
!strcmp(constraint->choice2, "*")))
{
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
goto error;
}
if (constraint->option1[0] == '*')
_cups_strcpy(constraint->option1, constraint->option1 + 1);
else if (pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
goto error;
}
if (pg->ppd_conform == PPD_CONFORM_STRICT &&
constraint->choice1[0] == '*')
{
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
goto error;
}
if (constraint->option2[0] == '*')
_cups_strcpy(constraint->option2, constraint->option2 + 1);
else if (pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
goto error;
}
if (pg->ppd_conform == PPD_CONFORM_STRICT &&
constraint->choice2[0] == '*')
{
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
goto error;
}
break;
}
/*
* Don't add this one as an attribute...
*/
free(string);
string = NULL;
}
else if (!strcmp(keyword, "PaperDimension"))
{
if (!_cups_strcasecmp(name, "custom") || !_cups_strncasecmp(name, "custom.", 7))
{
char cname[PPD_MAX_NAME]; /* Rewrite with a leading underscore */
snprintf(cname, sizeof(cname), "_%s", name);
strlcpy(name, cname, sizeof(name));
}
if ((size = ppdPageSize(ppd, name)) == NULL)
size = ppd_add_size(ppd, name);
if (size == NULL)
{
/*
* Unable to add or find size!
*/
pg->ppd_status = PPD_ALLOC_ERROR;
goto error;
}
size->width = (float)_cupsStrScand(string, &sptr, loc);
size->length = (float)_cupsStrScand(sptr, NULL, loc);
free(string);
string = NULL;
}
else if (!strcmp(keyword, "ImageableArea"))
{
if (!_cups_strcasecmp(name, "custom") || !_cups_strncasecmp(name, "custom.", 7))
{
char cname[PPD_MAX_NAME]; /* Rewrite with a leading underscore */
snprintf(cname, sizeof(cname), "_%s", name);
strlcpy(name, cname, sizeof(name));
}
if ((size = ppdPageSize(ppd, name)) == NULL)
size = ppd_add_size(ppd, name);
if (size == NULL)
{
/*
* Unable to add or find size!
*/
pg->ppd_status = PPD_ALLOC_ERROR;
goto error;
}
size->left = (float)_cupsStrScand(string, &sptr, loc);
size->bottom = (float)_cupsStrScand(sptr, &sptr, loc);
size->right = (float)_cupsStrScand(sptr, &sptr, loc);
size->top = (float)_cupsStrScand(sptr, NULL, loc);
free(string);
string = NULL;
}
else if (option != NULL &&
(mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) ==
(PPD_KEYWORD | PPD_OPTION | PPD_STRING) &&
!strcmp(keyword, option->keyword))
{
DEBUG_printf(("2_ppdOpen: group=%p, subgroup=%p", group, subgroup));
if (!_cups_strcasecmp(name, "custom") || !_cups_strncasecmp(name, "custom.", 7))
{
char cname[PPD_MAX_NAME]; /* Rewrite with a leading underscore */
snprintf(cname, sizeof(cname), "_%s", name);
strlcpy(name, cname, sizeof(name));
}
if (!strcmp(keyword, "PageSize"))
{
/*
* Add a page size...
*/
if (ppdPageSize(ppd, name) == NULL)
ppd_add_size(ppd, name);
}
/*
* Add the option choice...
*/
if ((choice = ppd_add_choice(option, name)) == NULL)
{
pg->ppd_status = PPD_ALLOC_ERROR;
goto error;
}
if (text[0])
cupsCharsetToUTF8((cups_utf8_t *)choice->text, text,
sizeof(choice->text), encoding);
else if (!strcmp(name, "True"))
strlcpy(choice->text, _("Yes"), sizeof(choice->text));
else if (!strcmp(name, "False"))
strlcpy(choice->text, _("No"), sizeof(choice->text));
else
strlcpy(choice->text, name, sizeof(choice->text));
if (option->section == PPD_ORDER_JCL)
ppd_decode(string); /* Decode quoted string */
choice->code = string;
string = NULL; /* Don't add as an attribute below */
}
/*
* Add remaining lines with keywords and string values as attributes...
*/
if (string &&
(mask & (PPD_KEYWORD | PPD_STRING)) == (PPD_KEYWORD | PPD_STRING))
ppd_add_attr(ppd, keyword, name, text, string);
else
free(string);
}
/*
* Check for a missing CloseUI/JCLCloseUI...
*/
if (option && pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_status = PPD_MISSING_CLOSE_UI;
goto error;
}
/*
* Check for a missing CloseGroup...
*/
if (group && pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_status = PPD_MISSING_CLOSE_GROUP;
goto error;
}
free(line.buffer);
/*
* Reset language preferences...
*/
#ifdef DEBUG
if (!cupsFileEOF(fp))
DEBUG_printf(("1_ppdOpen: Premature EOF at %lu...\n",
(unsigned long)cupsFileTell(fp)));
#endif /* DEBUG */
if (pg->ppd_status != PPD_OK)
{
/*
* Had an error reading the PPD file, cannot continue!
*/
ppdClose(ppd);
return (NULL);
}
/*
* Update the filters array as needed...
*/
if (!ppd_update_filters(ppd, pg))
{
ppdClose(ppd);
return (NULL);
}
/*
* Create the sorted options array and set the option back-pointer for
* each choice and custom option...
*/
ppd->options = cupsArrayNew2((cups_array_func_t)ppd_compare_options, NULL,
(cups_ahash_func_t)ppd_hash_option,
PPD_HASHSIZE);
for (i = ppd->num_groups, group = ppd->groups;
i > 0;
i --, group ++)
{
for (j = group->num_options, option = group->options;
j > 0;
j --, option ++)
{
ppd_coption_t *coption; /* Custom option */
cupsArrayAdd(ppd->options, option);
for (k = 0; k < option->num_choices; k ++)
option->choices[k].option = option;
if ((coption = ppdFindCustomOption(ppd, option->keyword)) != NULL)
coption->option = option;
}
}
/*
* Create an array to track the marked choices...
*/
ppd->marked = cupsArrayNew((cups_array_func_t)ppd_compare_choices, NULL);
/*
* Return the PPD file structure...
*/
return (ppd);
/*
* Common exit point for errors to save code size...
*/
error:
free(string);
free(line.buffer);
ppdClose(ppd);
return (NULL);
}
/*
* 'ppdOpen()' - Read a PPD file into memory.
*/
ppd_file_t * /* O - PPD file record */
ppdOpen(FILE *fp) /* I - File to read from */
{
ppd_file_t *ppd; /* PPD file record */
cups_file_t *cf; /* CUPS file */
/*
* Reopen the stdio file as a CUPS file...
*/
if ((cf = cupsFileOpenFd(fileno(fp), "r")) == NULL)
return (NULL);
/*
* Load the PPD file using the newer API...
*/
ppd = _ppdOpen(cf, _PPD_LOCALIZATION_DEFAULT);
/*
* Close the CUPS file and return the PPD...
*/
cupsFileClose(cf);
return (ppd);
}
/*
* 'ppdOpen2()' - Read a PPD file into memory.
*
* @since CUPS 1.2/macOS 10.5@
*/
ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
ppdOpen2(cups_file_t *fp) /* I - File to read from */
{
return _ppdOpen(fp, _PPD_LOCALIZATION_DEFAULT);
}
/*
* 'ppdOpenFd()' - Read a PPD file into memory.
*/
ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
ppdOpenFd(int fd) /* I - File to read from */
{
cups_file_t *fp; /* CUPS file pointer */
ppd_file_t *ppd; /* PPD file record */
_ppd_globals_t *pg = _ppdGlobals();
/* Global data */
/*
* Set the line number to 0...
*/
pg->ppd_line = 0;
/*
* Range check input...
*/
if (fd < 0)
{
pg->ppd_status = PPD_NULL_FILE;
return (NULL);
}
/*
* Try to open the file and parse it...
*/
if ((fp = cupsFileOpenFd(fd, "r")) != NULL)
{
ppd = ppdOpen2(fp);
cupsFileClose(fp);
}
else
{
pg->ppd_status = PPD_FILE_OPEN_ERROR;
ppd = NULL;
}
return (ppd);
}
/*
* '_ppdOpenFile()' - Read a PPD file into memory.
*/
ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
_ppdOpenFile(const char *filename, /* I - File to read from */
_ppd_localization_t localization) /* I - Localization to load */
{
cups_file_t *fp; /* File pointer */
ppd_file_t *ppd; /* PPD file record */
_ppd_globals_t *pg = _ppdGlobals();
/* Global data */
/*
* Set the line number to 0...
*/
pg->ppd_line = 0;
/*
* Range check input...
*/
if (filename == NULL)
{
pg->ppd_status = PPD_NULL_FILE;
return (NULL);
}
/*
* Try to open the file and parse it...
*/
if ((fp = cupsFileOpen(filename, "r")) != NULL)
{
ppd = _ppdOpen(fp, localization);
cupsFileClose(fp);
}
else
{
pg->ppd_status = PPD_FILE_OPEN_ERROR;
ppd = NULL;
}
return (ppd);
}
/*
* 'ppdOpenFile()' - Read a PPD file into memory.
*/
ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
ppdOpenFile(const char *filename) /* I - File to read from */
{
return _ppdOpenFile(filename, _PPD_LOCALIZATION_DEFAULT);
}
/*
* 'ppdSetConformance()' - Set the conformance level for PPD files.
*
* @since CUPS 1.1.20/macOS 10.4@
*/
void
ppdSetConformance(ppd_conform_t c) /* I - Conformance level */
{
_ppd_globals_t *pg = _ppdGlobals();
/* Global data */
pg->ppd_conform = c;
}
/*
* 'ppd_add_attr()' - Add an attribute to the PPD data.
*/
static ppd_attr_t * /* O - New attribute */
ppd_add_attr(ppd_file_t *ppd, /* I - PPD file data */
const char *name, /* I - Attribute name */
const char *spec, /* I - Specifier string, if any */
const char *text, /* I - Text string, if any */
const char *value) /* I - Value of attribute */
{
ppd_attr_t **ptr, /* New array */
*temp; /* New attribute */
/*
* Range check input...
*/
if (ppd == NULL || name == NULL || spec == NULL)
return (NULL);
/*
* Create the array as needed...
*/
if (!ppd->sorted_attrs)
ppd->sorted_attrs = cupsArrayNew((cups_array_func_t)ppd_compare_attrs,
NULL);
/*
* Allocate memory for the new attribute...
*/
if (ppd->num_attrs == 0)
ptr = malloc(sizeof(ppd_attr_t *));
else
ptr = realloc(ppd->attrs, (size_t)(ppd->num_attrs + 1) * sizeof(ppd_attr_t *));
if (ptr == NULL)
return (NULL);
ppd->attrs = ptr;
ptr += ppd->num_attrs;
if ((temp = calloc(1, sizeof(ppd_attr_t))) == NULL)
return (NULL);
*ptr = temp;
ppd->num_attrs ++;
/*
* Copy data over...
*/
strlcpy(temp->name, name, sizeof(temp->name));
strlcpy(temp->spec, spec, sizeof(temp->spec));
strlcpy(temp->text, text, sizeof(temp->text));
temp->value = (char *)value;
/*
* Add the attribute to the sorted array...
*/
cupsArrayAdd(ppd->sorted_attrs, temp);
/*
* Return the attribute...
*/
return (temp);
}
/*
* 'ppd_add_choice()' - Add a choice to an option.
*/
static ppd_choice_t * /* O - Named choice */
ppd_add_choice(ppd_option_t *option, /* I - Option */
const char *name) /* I - Name of choice */
{
ppd_choice_t *choice; /* Choice */
if (option->num_choices == 0)
choice = malloc(sizeof(ppd_choice_t));
else
choice = realloc(option->choices, sizeof(ppd_choice_t) * (size_t)(option->num_choices + 1));
if (choice == NULL)
return (NULL);
option->choices = choice;
choice += option->num_choices;
option->num_choices ++;
memset(choice, 0, sizeof(ppd_choice_t));
strlcpy(choice->choice, name, sizeof(choice->choice));
return (choice);
}
/*
* 'ppd_add_size()' - Add a page size.
*/
static ppd_size_t * /* O - Named size */
ppd_add_size(ppd_file_t *ppd, /* I - PPD file */
const char *name) /* I - Name of size */
{
ppd_size_t *size; /* Size */
if (ppd->num_sizes == 0)
size = malloc(sizeof(ppd_size_t));
else
size = realloc(ppd->sizes, sizeof(ppd_size_t) * (size_t)(ppd->num_sizes + 1));
if (size == NULL)
return (NULL);
ppd->sizes = size;
size += ppd->num_sizes;
ppd->num_sizes ++;
memset(size, 0, sizeof(ppd_size_t));
strlcpy(size->name, name, sizeof(size->name));
return (size);
}
/*
* 'ppd_compare_attrs()' - Compare two attributes.
*/
static int /* O - Result of comparison */
ppd_compare_attrs(ppd_attr_t *a, /* I - First attribute */
ppd_attr_t *b) /* I - Second attribute */
{
return (_cups_strcasecmp(a->name, b->name));
}
/*
* 'ppd_compare_choices()' - Compare two choices...
*/
static int /* O - Result of comparison */
ppd_compare_choices(ppd_choice_t *a, /* I - First choice */
ppd_choice_t *b) /* I - Second choice */
{
return (strcmp(a->option->keyword, b->option->keyword));
}
/*
* 'ppd_compare_coptions()' - Compare two custom options.
*/
static int /* O - Result of comparison */
ppd_compare_coptions(ppd_coption_t *a, /* I - First option */
ppd_coption_t *b) /* I - Second option */
{
return (_cups_strcasecmp(a->keyword, b->keyword));
}
/*
* 'ppd_compare_options()' - Compare two options.
*/
static int /* O - Result of comparison */
ppd_compare_options(ppd_option_t *a, /* I - First option */
ppd_option_t *b) /* I - Second option */
{
return (_cups_strcasecmp(a->keyword, b->keyword));
}
/*
* 'ppd_decode()' - Decode a string value...
*/
static int /* O - Length of decoded string */
ppd_decode(char *string) /* I - String to decode */
{
char *inptr, /* Input pointer */
*outptr; /* Output pointer */
inptr = string;
outptr = string;
while (*inptr != '\0')
if (*inptr == '<' && isxdigit(inptr[1] & 255))
{
/*
* Convert hex to 8-bit values...
*/
inptr ++;
while (isxdigit(*inptr & 255))
{
if (_cups_isalpha(*inptr))
*outptr = (char)((tolower(*inptr) - 'a' + 10) << 4);
else
*outptr = (char)((*inptr - '0') << 4);
inptr ++;
if (!isxdigit(*inptr & 255))
break;
if (_cups_isalpha(*inptr))
*outptr |= (char)(tolower(*inptr) - 'a' + 10);
else
*outptr |= (char)(*inptr - '0');
inptr ++;
outptr ++;
}
while (*inptr != '>' && *inptr != '\0')
inptr ++;
while (*inptr == '>')
inptr ++;
}
else
*outptr++ = *inptr++;
*outptr = '\0';
return ((int)(outptr - string));
}
/*
* 'ppd_free_filters()' - Free the filters array.
*/
static void
ppd_free_filters(ppd_file_t *ppd) /* I - PPD file */
{
int i; /* Looping var */
char **filter; /* Current filter */
if (ppd->num_filters > 0)
{
for (i = ppd->num_filters, filter = ppd->filters; i > 0; i --, filter ++)
free(*filter);
free(ppd->filters);
ppd->num_filters = 0;
ppd->filters = NULL;
}
}
/*
* 'ppd_free_group()' - Free a single UI group.
*/
static void
ppd_free_group(ppd_group_t *group) /* I - Group to free */
{
int i; /* Looping var */
ppd_option_t *option; /* Current option */
ppd_group_t *subgroup; /* Current sub-group */
if (group->num_options > 0)
{
for (i = group->num_options, option = group->options;
i > 0;
i --, option ++)
ppd_free_option(option);
free(group->options);
}
if (group->num_subgroups > 0)
{
for (i = group->num_subgroups, subgroup = group->subgroups;
i > 0;
i --, subgroup ++)
ppd_free_group(subgroup);
free(group->subgroups);
}
}
/*
* 'ppd_free_option()' - Free a single option.
*/
static void
ppd_free_option(ppd_option_t *option) /* I - Option to free */
{
int i; /* Looping var */
ppd_choice_t *choice; /* Current choice */
if (option->num_choices > 0)
{
for (i = option->num_choices, choice = option->choices;
i > 0;
i --, choice ++)
{
free(choice->code);
}
free(option->choices);
}
}
/*
* 'ppd_get_coption()' - Get a custom option record.
*/
static ppd_coption_t * /* O - Custom option... */
ppd_get_coption(ppd_file_t *ppd, /* I - PPD file */
const char *name) /* I - Name of option */
{
ppd_coption_t *copt; /* New custom option */
/*
* See if the option already exists...
*/
if ((copt = ppdFindCustomOption(ppd, name)) != NULL)
return (copt);
/*
* Not found, so create the custom option record...
*/
if ((copt = calloc(1, sizeof(ppd_coption_t))) == NULL)
return (NULL);
strlcpy(copt->keyword, name, sizeof(copt->keyword));
copt->params = cupsArrayNew((cups_array_func_t)NULL, NULL);
cupsArrayAdd(ppd->coptions, copt);
/*
* Return the new record...
*/
return (copt);
}
/*
* 'ppd_get_cparam()' - Get a custom parameter record.
*/
static ppd_cparam_t * /* O - Extended option... */
ppd_get_cparam(ppd_coption_t *opt, /* I - PPD file */
const char *param, /* I - Name of parameter */
const char *text) /* I - Human-readable text */
{
ppd_cparam_t *cparam; /* New custom parameter */
/*
* See if the parameter already exists...
*/
if ((cparam = ppdFindCustomParam(opt, param)) != NULL)
return (cparam);
/*
* Not found, so create the custom parameter record...
*/
if ((cparam = calloc(1, sizeof(ppd_cparam_t))) == NULL)
return (NULL);
cparam->type = PPD_CUSTOM_UNKNOWN;
strlcpy(cparam->name, param, sizeof(cparam->name));
strlcpy(cparam->text, text[0] ? text : param, sizeof(cparam->text));
/*
* Add this record to the array...
*/
cupsArrayAdd(opt->params, cparam);
/*
* Return the new record...
*/
return (cparam);
}
/*
* 'ppd_get_group()' - Find or create the named group as needed.
*/
static ppd_group_t * /* O - Named group */
ppd_get_group(ppd_file_t *ppd, /* I - PPD file */
const char *name, /* I - Name of group */
const char *text, /* I - Text for group */
_ppd_globals_t *pg, /* I - Global data */
cups_encoding_t encoding) /* I - Encoding of text */
{
int i; /* Looping var */
ppd_group_t *group; /* Group */
DEBUG_printf(("7ppd_get_group(ppd=%p, name=\"%s\", text=\"%s\", cg=%p)",
ppd, name, text, pg));
for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
if (!strcmp(group->name, name))
break;
if (i == 0)
{
DEBUG_printf(("8ppd_get_group: Adding group %s...", name));
if (pg->ppd_conform == PPD_CONFORM_STRICT && strlen(text) >= sizeof(group->text))
{
pg->ppd_status = PPD_ILLEGAL_TRANSLATION;
return (NULL);
}
if (ppd->num_groups == 0)
group = malloc(sizeof(ppd_group_t));
else
group = realloc(ppd->groups, (size_t)(ppd->num_groups + 1) * sizeof(ppd_group_t));
if (group == NULL)
{
pg->ppd_status = PPD_ALLOC_ERROR;
return (NULL);
}
ppd->groups = group;
group += ppd->num_groups;
ppd->num_groups ++;
memset(group, 0, sizeof(ppd_group_t));
strlcpy(group->name, name, sizeof(group->name));
cupsCharsetToUTF8((cups_utf8_t *)group->text, text,
sizeof(group->text), encoding);
}
return (group);
}
/*
* 'ppd_get_option()' - Find or create the named option as needed.
*/
static ppd_option_t * /* O - Named option */
ppd_get_option(ppd_group_t *group, /* I - Group */
const char *name) /* I - Name of option */
{
int i; /* Looping var */
ppd_option_t *option; /* Option */
DEBUG_printf(("7ppd_get_option(group=%p(\"%s\"), name=\"%s\")",
group, group->name, name));
for (i = group->num_options, option = group->options; i > 0; i --, option ++)
if (!strcmp(option->keyword, name))
break;
if (i == 0)
{
if (group->num_options == 0)
option = malloc(sizeof(ppd_option_t));
else
option = realloc(group->options, (size_t)(group->num_options + 1) * sizeof(ppd_option_t));
if (option == NULL)
return (NULL);
group->options = option;
option += group->num_options;
group->num_options ++;
memset(option, 0, sizeof(ppd_option_t));
strlcpy(option->keyword, name, sizeof(option->keyword));
}
return (option);
}
/*
* 'ppd_globals_alloc()' - Allocate and initialize global data.
*/
static _ppd_globals_t * /* O - Pointer to global data */
ppd_globals_alloc(void)
{
return ((_ppd_globals_t *)calloc(1, sizeof(_ppd_globals_t)));
}
/*
* 'ppd_globals_free()' - Free global data.
*/
#if defined(HAVE_PTHREAD_H) || defined(_WIN32)
static void
ppd_globals_free(_ppd_globals_t *pg) /* I - Pointer to global data */
{
free(pg);
}
#endif /* HAVE_PTHREAD_H || _WIN32 */
#ifdef HAVE_PTHREAD_H
/*
* 'ppd_globals_init()' - Initialize per-thread globals...
*/
static void
ppd_globals_init(void)
{
/*
* Register the global data for this thread...
*/
pthread_key_create(&ppd_globals_key, (void (*)(void *))ppd_globals_free);
}
#endif /* HAVE_PTHREAD_H */
/*
* 'ppd_hash_option()' - Generate a hash of the option name...
*/
static int /* O - Hash index */
ppd_hash_option(ppd_option_t *option) /* I - Option */
{
int hash = 0; /* Hash index */
const char *k; /* Pointer into keyword */
for (hash = option->keyword[0], k = option->keyword + 1; *k;)
hash = 33 * hash + *k++;
return (hash & 511);
}
/*
* 'ppd_read()' - Read a line from a PPD file, skipping comment lines as
* necessary.
*/
static int /* O - Bitmask of fields read */
ppd_read(cups_file_t *fp, /* I - File to read from */
_ppd_line_t *line, /* I - Line buffer */
char *keyword, /* O - Keyword from line */
char *option, /* O - Option from line */
char *text, /* O - Human-readable text from line */
char **string, /* O - Code/string data */
int ignoreblank, /* I - Ignore blank lines? */
_ppd_globals_t *pg) /* I - Global data */
{
int ch, /* Character from file */
col, /* Column in line */
colon, /* Colon seen? */
endquote, /* Waiting for an end quote */
mask, /* Mask to be returned */
startline, /* Start line */
textlen; /* Length of text */
char *keyptr, /* Keyword pointer */
*optptr, /* Option pointer */
*textptr, /* Text pointer */
*strptr, /* Pointer into string */
*lineptr; /* Current position in line buffer */
/*
* Now loop until we have a valid line...
*/
*string = NULL;
col = 0;
startline = pg->ppd_line + 1;
if (!line->buffer)
{
line->bufsize = 1024;
line->buffer = malloc(1024);
if (!line->buffer)
return (0);
}
do
{
/*
* Read the line...
*/
lineptr = line->buffer;
endquote = 0;
colon = 0;
while ((ch = cupsFileGetChar(fp)) != EOF)
{
if (lineptr >= (line->buffer + line->bufsize - 1))
{
/*
* Expand the line buffer...
*/
char *temp; /* Temporary line pointer */
line->bufsize += 1024;
if (line->bufsize > 262144)
{
/*
* Don't allow lines longer than 256k!
*/
pg->ppd_line = startline;
pg->ppd_status = PPD_LINE_TOO_LONG;
return (0);
}
temp = realloc(line->buffer, line->bufsize);
if (!temp)
{
pg->ppd_line = startline;
pg->ppd_status = PPD_LINE_TOO_LONG;
return (0);
}
lineptr = temp + (lineptr - line->buffer);
line->buffer = temp;
}
if (ch == '\r' || ch == '\n')
{
/*
* Line feed or carriage return...
*/
pg->ppd_line ++;
col = 0;
if (ch == '\r')
{
/*
* Check for a trailing line feed...
*/
if ((ch = cupsFilePeekChar(fp)) == EOF)
{
ch = '\n';
break;
}
if (ch == 0x0a)
cupsFileGetChar(fp);
}
if (lineptr == line->buffer && ignoreblank)
continue; /* Skip blank lines */
ch = '\n';
if (!endquote) /* Continue for multi-line text */
break;
*lineptr++ = '\n';
}
else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
{
/*
* Other control characters...
*/
pg->ppd_line = startline;
pg->ppd_status = PPD_ILLEGAL_CHARACTER;
return (0);
}
else if (ch != 0x1a)
{
/*
* Any other character...
*/
*lineptr++ = (char)ch;
col ++;
if (col > (PPD_MAX_LINE - 1))
{
/*
* Line is too long...
*/
pg->ppd_line = startline;
pg->ppd_status = PPD_LINE_TOO_LONG;
return (0);
}
if (ch == ':' && strncmp(line->buffer, "*%", 2) != 0)
colon = 1;
if (ch == '\"' && colon)
endquote = !endquote;
}
}
if (endquote)
{
/*
* Didn't finish this quoted string...
*/
while ((ch = cupsFileGetChar(fp)) != EOF)
if (ch == '\"')
break;
else if (ch == '\r' || ch == '\n')
{
pg->ppd_line ++;
col = 0;
if (ch == '\r')
{
/*
* Check for a trailing line feed...
*/
if ((ch = cupsFilePeekChar(fp)) == EOF)
break;
if (ch == 0x0a)
cupsFileGetChar(fp);
}
}
else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
{
/*
* Other control characters...
*/
pg->ppd_line = startline;
pg->ppd_status = PPD_ILLEGAL_CHARACTER;
return (0);
}
else if (ch != 0x1a)
{
col ++;
if (col > (PPD_MAX_LINE - 1))
{
/*
* Line is too long...
*/
pg->ppd_line = startline;
pg->ppd_status = PPD_LINE_TOO_LONG;
return (0);
}
}
}
if (ch != '\n')
{
/*
* Didn't finish this line...
*/
while ((ch = cupsFileGetChar(fp)) != EOF)
if (ch == '\r' || ch == '\n')
{
/*
* Line feed or carriage return...
*/
pg->ppd_line ++;
col = 0;
if (ch == '\r')
{
/*
* Check for a trailing line feed...
*/
if ((ch = cupsFilePeekChar(fp)) == EOF)
break;
if (ch == 0x0a)
cupsFileGetChar(fp);
}
break;
}
else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
{
/*
* Other control characters...
*/
pg->ppd_line = startline;
pg->ppd_status = PPD_ILLEGAL_CHARACTER;
return (0);
}
else if (ch != 0x1a)
{
col ++;
if (col > (PPD_MAX_LINE - 1))
{
/*
* Line is too long...
*/
pg->ppd_line = startline;
pg->ppd_status = PPD_LINE_TOO_LONG;
return (0);
}
}
}
if (lineptr > line->buffer && lineptr[-1] == '\n')
lineptr --;
*lineptr = '\0';
DEBUG_printf(("9ppd_read: LINE=\"%s\"", line->buffer));
/*
* The dynamically created PPDs for older style macOS
* drivers include a large blob of data inserted as comments
* at the end of the file. As an optimization we can stop
* reading the PPD when we get to the start of this data.
*/
if (!strcmp(line->buffer, "*%APLWORKSET START"))
return (0);
if (ch == EOF && lineptr == line->buffer)
return (0);
/*
* Now parse it...
*/
mask = 0;
lineptr = line->buffer + 1;
keyword[0] = '\0';
option[0] = '\0';
text[0] = '\0';
*string = NULL;
if ((!line->buffer[0] || /* Blank line */
!strncmp(line->buffer, "*%", 2) || /* Comment line */
!strcmp(line->buffer, "*End")) && /* End of multi-line string */
ignoreblank) /* Ignore these? */
{
startline = pg->ppd_line + 1;
continue;
}
if (!strcmp(line->buffer, "*")) /* (Bad) comment line */
{
if (pg->ppd_conform == PPD_CONFORM_RELAXED)
{
startline = pg->ppd_line + 1;
continue;
}
else
{
pg->ppd_line = startline;
pg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
return (0);
}
}
if (line->buffer[0] != '*') /* All lines start with an asterisk */
{
/*
* Allow lines consisting of just whitespace...
*/
for (lineptr = line->buffer; *lineptr; lineptr ++)
if (*lineptr && !_cups_isspace(*lineptr))
break;
if (*lineptr)
{
pg->ppd_status = PPD_MISSING_ASTERISK;
return (0);
}
else if (ignoreblank)
continue;
else
return (0);
}
/*
* Get a keyword...
*/
keyptr = keyword;
while (*lineptr && *lineptr != ':' && !_cups_isspace(*lineptr))
{
if (*lineptr <= ' ' || *lineptr > 126 || *lineptr == '/' ||
(keyptr - keyword) >= (PPD_MAX_NAME - 1))
{
pg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
return (0);
}
*keyptr++ = *lineptr++;
}
*keyptr = '\0';
if (!strcmp(keyword, "End"))
continue;
mask |= PPD_KEYWORD;
if (_cups_isspace(*lineptr))
{
/*
* Get an option name...
*/
while (_cups_isspace(*lineptr))
lineptr ++;
optptr = option;
while (*lineptr && !_cups_isspace(*lineptr) && *lineptr != ':' &&
*lineptr != '/')
{
if (*lineptr <= ' ' || *lineptr > 126 ||
(optptr - option) >= (PPD_MAX_NAME - 1))
{
pg->ppd_status = PPD_ILLEGAL_OPTION_KEYWORD;
return (0);
}
*optptr++ = *lineptr++;
}
*optptr = '\0';
if (_cups_isspace(*lineptr) && pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_status = PPD_ILLEGAL_WHITESPACE;
return (0);
}
while (_cups_isspace(*lineptr))
lineptr ++;
mask |= PPD_OPTION;
if (*lineptr == '/')
{
/*
* Get human-readable text...
*/
lineptr ++;
textptr = text;
while (*lineptr != '\0' && *lineptr != '\n' && *lineptr != ':')
{
if (((unsigned char)*lineptr < ' ' && *lineptr != '\t') ||
(textptr - text) >= (PPD_MAX_LINE - 1))
{
pg->ppd_status = PPD_ILLEGAL_TRANSLATION;
return (0);
}
*textptr++ = *lineptr++;
}
*textptr = '\0';
textlen = ppd_decode(text);
if (textlen > PPD_MAX_TEXT && pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_status = PPD_ILLEGAL_TRANSLATION;
return (0);
}
mask |= PPD_TEXT;
}
}
if (_cups_isspace(*lineptr) && pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_status = PPD_ILLEGAL_WHITESPACE;
return (0);
}
while (_cups_isspace(*lineptr))
lineptr ++;
if (*lineptr == ':')
{
/*
* Get string after triming leading and trailing whitespace...
*/
lineptr ++;
while (_cups_isspace(*lineptr))
lineptr ++;
strptr = lineptr + strlen(lineptr) - 1;
while (strptr >= lineptr && _cups_isspace(*strptr))
*strptr-- = '\0';
if (*strptr == '\"')
{
/*
* Quoted string by itself, remove quotes...
*/
*strptr = '\0';
lineptr ++;
}
*string = strdup(lineptr);
mask |= PPD_STRING;
}
}
while (mask == 0);
return (mask);
}
/*
* 'ppd_update_filters()' - Update the filters array as needed.
*
* This function re-populates the filters array with cupsFilter2 entries that
* have been stripped of the destination MIME media types and any maxsize hints.
*
* (All for backwards-compatibility)
*/
static int /* O - 1 on success, 0 on failure */
ppd_update_filters(ppd_file_t *ppd, /* I - PPD file */
_ppd_globals_t *pg) /* I - Global data */
{
ppd_attr_t *attr; /* Current cupsFilter2 value */
char srcsuper[16], /* Source MIME media type */
srctype[256],
dstsuper[16], /* Destination MIME media type */
dsttype[256],
program[1024], /* Command to run */
*ptr, /* Pointer into command to run */
buffer[1024], /* Re-written cupsFilter value */
**filter; /* Current filter */
int cost; /* Cost of filter */
DEBUG_printf(("4ppd_update_filters(ppd=%p, cg=%p)", ppd, pg));
/*
* See if we have any cupsFilter2 lines...
*/
if ((attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) == NULL)
{
DEBUG_puts("5ppd_update_filters: No cupsFilter2 keywords present.");
return (1);
}
/*
* Yes, free the cupsFilter-defined filters and re-build...
*/
ppd_free_filters(ppd);
do
{
/*
* Parse the cupsFilter2 string:
*
* src/type dst/type cost program
* src/type dst/type cost maxsize(n) program
*/
DEBUG_printf(("5ppd_update_filters: cupsFilter2=\"%s\"", attr->value));
if (sscanf(attr->value, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]",
srcsuper, srctype, dstsuper, dsttype, &cost, program) != 6)
{
DEBUG_puts("5ppd_update_filters: Bad cupsFilter2 line.");
pg->ppd_status = PPD_BAD_VALUE;
return (0);
}
DEBUG_printf(("5ppd_update_filters: srcsuper=\"%s\", srctype=\"%s\", "
"dstsuper=\"%s\", dsttype=\"%s\", cost=%d, program=\"%s\"",
srcsuper, srctype, dstsuper, dsttype, cost, program));
if (!strncmp(program, "maxsize(", 8) &&
(ptr = strchr(program + 8, ')')) != NULL)
{
DEBUG_puts("5ppd_update_filters: Found maxsize(nnn).");
ptr ++;
while (_cups_isspace(*ptr))
ptr ++;
_cups_strcpy(program, ptr);
DEBUG_printf(("5ppd_update_filters: New program=\"%s\"", program));
}
/*
* Convert to cupsFilter format:
*
* src/type cost program
*/
snprintf(buffer, sizeof(buffer), "%s/%s %d %s", srcsuper, srctype, cost,
program);
DEBUG_printf(("5ppd_update_filters: Adding \"%s\".", buffer));
/*
* Add a cupsFilter-compatible string to the filters array.
*/
if (ppd->num_filters == 0)
filter = malloc(sizeof(char *));
else
filter = realloc(ppd->filters, sizeof(char *) * (size_t)(ppd->num_filters + 1));
if (filter == NULL)
{
DEBUG_puts("5ppd_update_filters: Out of memory.");
pg->ppd_status = PPD_ALLOC_ERROR;
return (0);
}
ppd->filters = filter;
filter += ppd->num_filters;
ppd->num_filters ++;
*filter = strdup(buffer);
}
while ((attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL);
DEBUG_puts("5ppd_update_filters: Completed OK.");
return (1);
}