#include "cups-private.h"
#include "ppd-private.h"
#define ppd_free(p) if (p) free(p)
#define PPD_KEYWORD 1
#define PPD_OPTION 2
#define PPD_TEXT 4
#define PPD_STRING 8
#define PPD_HASHSIZE 512
typedef struct _ppd_line_s
{
char *buffer;
size_t bufsize;
} _ppd_line_t;
static _cups_threadkey_t ppd_globals_key = _CUPS_THREADKEY_INITIALIZER;
#ifdef HAVE_PTHREAD_H
static pthread_once_t ppd_globals_key_once = PTHREAD_ONCE_INIT;
#endif
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
#ifdef HAVE_PTHREAD_H
static void ppd_globals_init(void);
#endif
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);
void
ppdClose(ppd_file_t *ppd)
{
int i;
ppd_emul_t *emul;
ppd_group_t *group;
char **font;
ppd_attr_t **attr;
ppd_coption_t *coption;
ppd_cparam_t *cparam;
if (!ppd)
return;
_cupsStrFree(ppd->lang_encoding);
_cupsStrFree(ppd->nickname);
if (ppd->patches)
free(ppd->patches);
_cupsStrFree(ppd->jcl_begin);
_cupsStrFree(ppd->jcl_end);
_cupsStrFree(ppd->jcl_ps);
if (ppd->num_emulations > 0)
{
for (i = ppd->num_emulations, emul = ppd->emulations; i > 0; i --, emul ++)
{
_cupsStrFree(emul->start);
_cupsStrFree(emul->stop);
}
ppd_free(ppd->emulations);
}
if (ppd->num_groups > 0)
{
for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
ppd_free_group(group);
ppd_free(ppd->groups);
}
cupsArrayDelete(ppd->options);
cupsArrayDelete(ppd->marked);
if (ppd->num_sizes > 0)
ppd_free(ppd->sizes);
if (ppd->num_consts > 0)
ppd_free(ppd->consts);
ppd_free_filters(ppd);
if (ppd->num_fonts > 0)
{
for (i = ppd->num_fonts, font = ppd->fonts; i > 0; i --, font ++)
_cupsStrFree(*font);
ppd_free(ppd->fonts);
}
if (ppd->num_profiles > 0)
ppd_free(ppd->profiles);
if (ppd->num_attrs > 0)
{
for (i = ppd->num_attrs, attr = ppd->attrs; i > 0; i --, attr ++)
{
_cupsStrFree((*attr)->value);
ppd_free(*attr);
}
ppd_free(ppd->attrs);
}
cupsArrayDelete(ppd->sorted_attrs);
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 :
_cupsStrFree(cparam->current.custom_string);
break;
default :
break;
}
free(cparam);
}
cupsArrayDelete(coption->params);
free(coption);
}
cupsArrayDelete(ppd->coptions);
if (ppd->cups_uiconstraints)
{
_ppd_cups_uiconsts_t *consts;
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);
}
if (ppd->cache)
_ppdCacheDestroy(ppd->cache);
ppd_free(ppd);
}
const char *
ppdErrorString(ppd_status_t status)
{
static const char * const 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")
};
if (status < PPD_OK || status >= PPD_MAX_STATUS)
return (_cupsLangString(cupsLangDefault(), _("Unknown")));
else
return (_cupsLangString(cupsLangDefault(), messages[status]));
}
cups_encoding_t
_ppdGetEncoding(const char *name)
{
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);
}
_ppd_globals_t *
_ppdGlobals(void)
{
_ppd_globals_t *pg;
#ifdef HAVE_PTHREAD_H
pthread_once(&ppd_globals_key_once, ppd_globals_init);
#endif
if ((pg = (_ppd_globals_t *)_cupsThreadGetData(ppd_globals_key)) == NULL)
{
if ((pg = ppd_globals_alloc()) != NULL)
_cupsThreadSetData(ppd_globals_key, pg);
}
return (pg);
}
ppd_status_t
ppdLastError(int *line)
{
_ppd_globals_t *pg = _ppdGlobals();
if (line)
*line = pg->ppd_line;
return (pg->ppd_status);
}
ppd_file_t *
_ppdOpen(
cups_file_t *fp,
_ppd_localization_t localization)
{
int i, j, k;
int count;
_ppd_line_t line;
ppd_file_t *ppd;
ppd_group_t *group,
*subgroup;
ppd_option_t *option;
ppd_choice_t *choice;
ppd_const_t *constraint;
ppd_size_t *size;
int mask;
char keyword[PPD_MAX_NAME],
name[PPD_MAX_NAME],
text[PPD_MAX_LINE],
*string,
*sptr,
*nameptr,
*temp,
**tempfonts;
float order;
ppd_section_t section;
ppd_profile_t *profile;
char **filter;
struct lconv *loc;
int ui_keyword;
cups_lang_t *lang;
cups_encoding_t encoding;
_ppd_globals_t *pg = _ppdGlobals();
char custom_name[PPD_MAX_NAME];
ppd_attr_t *custom_attr;
char ll[7],
ll_CC[7];
size_t ll_len = 0,
ll_CC_len = 0;
static const char * const ui_keywords[] =
{
#ifdef CUPS_USE_FULL_UI_KEYWORDS_LIST
"BlackSubstitution",
"Booklet",
"Collate",
"ManualFeed",
"MirrorPrint",
"NegativePrint",
"Sorter",
"TraySwitch",
"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
"PageRegion",
"PageSize"
#endif
};
static const char * const color_keywords[] =
{
".cupsICCProfile",
".ColorModel",
};
DEBUG_printf(("_ppdOpen(fp=%p)", fp));
pg->ppd_status = PPD_OK;
pg->ppd_line = 0;
if (fp == NULL)
{
pg->ppd_status = PPD_NULL_FILE;
return (NULL);
}
if (localization == _PPD_LOCALIZATION_DEFAULT)
{
if ((lang = cupsLangDefault()) == NULL)
return (NULL);
snprintf(ll_CC, sizeof(ll_CC), "%s.", lang->language);
if (!strcmp(lang->language, "zh_HK"))
strlcpy(ll, "zh_TW.", 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));
}
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')
{
if (pg->ppd_status == PPD_OK)
pg->ppd_status = PPD_MISSING_PPDADOBE4;
_cupsStrFree(string);
ppd_free(line.buffer);
return (NULL);
}
DEBUG_printf(("2_ppdOpen: keyword=%s, string=%p", keyword, string));
_cupsStrFree(string);
if ((ppd = calloc(1, sizeof(ppd_file_t))) == NULL)
{
pg->ppd_status = PPD_ALLOC_ERROR;
_cupsStrFree(string);
ppd_free(line.buffer);
return (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);
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)
{
pg->ppd_status = PPD_MISSING_VALUE;
goto error;
}
else if (!string)
continue;
if (ui_keyword)
{
option = NULL;
ui_keyword = 0;
}
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));
continue;
}
else if (localization == _PPD_LOCALIZATION_ICC_PROFILES)
{
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));
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])))
{
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;
}
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"))
{
ppd->lang_encoding = _cupsStrAlloc("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];
cupsCharsetToUTF8(utf8, string, sizeof(utf8), encoding);
ppd->nickname = _cupsStrAlloc((char *)utf8);
}
else
ppd->nickname = _cupsStrAlloc(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 = _cupsStrAlloc(string);
ppd_decode(ppd->jcl_begin);
}
else if (!strcmp(keyword, "JCLEnd"))
{
ppd->jcl_end = _cupsStrAlloc(string);
ppd_decode(ppd->jcl_end);
}
else if (!strcmp(keyword, "JCLToPSInterpreter"))
{
ppd->jcl_ps = _cupsStrAlloc(string);
ppd_decode(ppd->jcl_ps);
}
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 ++;
*filter = _cupsStrRetain(string);
}
else if (!strcmp(keyword, "Throughput"))
ppd->throughput = atoi(string);
else if (!strcmp(keyword, "Font"))
{
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] = _cupsStrAlloc(name);
ppd->num_fonts ++;
}
else if (!strncmp(keyword, "ParamCustom", 11))
{
ppd_coption_t *coption;
ppd_cparam_t *cparam;
int corder;
char ctype[33],
cminimum[65],
cmaximum[65];
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 (!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;
}
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;
DEBUG_puts("2_ppdOpen: Processing 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)
{
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 = _cupsStrAlloc(string);
if (custom_option->section == PPD_ORDER_JCL)
ppd_decode(choice->code);
}
if (!strcmp(keyword, "CustomPageSize"))
{
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)
{
for (count = 1, sptr = string; sptr != NULL;)
if ((sptr = strchr(sptr, ' ')) != NULL)
{
count ++;
while (*sptr == ' ')
sptr ++;
}
ppd->num_emulations = count;
if ((ppd->emulations = calloc((size_t)count, sizeof(ppd_emul_t))) == NULL)
{
pg->ppd_status = PPD_ALLOC_ERROR;
goto error;
}
for (i = 0, sptr = string; i < count; i ++)
{
for (nameptr = ppd->emulations[i].name;
*sptr != '\0' && *sptr != ' ';
sptr ++)
if (nameptr < (ppd->emulations[i].name + sizeof(ppd->emulations[i].name) - 1))
*nameptr++ = *sptr;
*nameptr = '\0';
while (*sptr == ' ')
sptr ++;
}
}
else if (!strncmp(keyword, "StartEmulator_", 14))
{
ppd_decode(string);
for (i = 0; i < ppd->num_emulations; i ++)
if (!strcmp(keyword + 14, ppd->emulations[i].name))
{
ppd->emulations[i].start = string;
string = NULL;
}
}
else if (!strncmp(keyword, "StopEmulator_", 13))
{
ppd_decode(string);
for (i = 0; i < ppd->num_emulations; i ++)
if (!strcmp(keyword + 13, ppd->emulations[i].name))
{
ppd->emulations[i].stop = string;
string = NULL;
}
}
else if (!strcmp(keyword, "JobPatchFile"))
{
if (isdigit(*string & 255))
{
for (sptr = string + 1; isdigit(*sptr & 255); sptr ++);
if (*sptr == ':')
{
pg->ppd_status = PPD_BAD_VALUE;
goto error;
}
}
if (!name[0] && pg->ppd_conform == PPD_CONFORM_STRICT)
{
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"))
{
if (option && pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_status = PPD_NESTED_OPEN_UI;
goto error;
}
DEBUG_printf(("2_ppdOpen: name=\"%s\" (%d)", name, (int)strlen(name)));
if (name[0] == '*')
_cups_strcpy(name, name + 1);
for (i = (int)strlen(name) - 1; i > 0 && _cups_isspace(name[i]); i --)
name[i] = '\0';
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;
}
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;
_cupsStrFree(string);
string = NULL;
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 = _cupsStrRetain(custom_attr->value);
}
}
else if (!strcmp(keyword, "JCLOpenUI"))
{
if (option && pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_status = PPD_NESTED_OPEN_UI;
goto error;
}
group = ppd_get_group(ppd, "JCL", _("JCL"), pg, encoding);
if (group == NULL)
goto error;
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;
}
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;
_cupsStrFree(string);
string = NULL;
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 = _cupsStrRetain(custom_attr->value);
}
}
else if (!strcmp(keyword, "CloseUI") || !strcmp(keyword, "JCLCloseUI"))
{
option = NULL;
_cupsStrFree(string);
string = NULL;
}
else if (!strcmp(keyword, "OpenGroup"))
{
if (group != NULL)
{
pg->ppd_status = PPD_NESTED_OPEN_GROUP;
goto error;
}
if (!string)
{
pg->ppd_status = PPD_BAD_OPEN_GROUP;
goto error;
}
if ((sptr = strchr(string, '/')) != NULL)
*sptr++ = '\0';
else
sptr = string;
ppd_decode(sptr);
group = ppd_get_group(ppd, string, sptr, pg, encoding);
if (group == NULL)
goto error;
_cupsStrFree(string);
string = NULL;
}
else if (!strcmp(keyword, "CloseGroup"))
{
group = NULL;
_cupsStrFree(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;
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;
}
_cupsStrFree(string);
string = NULL;
}
else if (!strncmp(keyword, "Default", 7))
{
if (string == NULL)
continue;
if (strchr(string, '/') != NULL)
*strchr(string, '/') = '\0';
if (!strcmp(keyword, "DefaultColorSpace"))
{
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))
{
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
{
ppd_option_t *toption;
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))
{
case 0 :
case 1 :
pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
goto error;
case 2 :
if (pg->ppd_conform == PPD_CONFORM_STRICT &&
(!strcmp(constraint->option1, "*") ||
!strcmp(constraint->choice1, "*")))
{
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 (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 :
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;
}
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 :
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;
}
_cupsStrFree(string);
string = NULL;
}
else if (!strcmp(keyword, "PaperDimension"))
{
if ((size = ppdPageSize(ppd, name)) == NULL)
size = ppd_add_size(ppd, name);
if (size == NULL)
{
pg->ppd_status = PPD_ALLOC_ERROR;
goto error;
}
size->width = (float)_cupsStrScand(string, &sptr, loc);
size->length = (float)_cupsStrScand(sptr, NULL, loc);
_cupsStrFree(string);
string = NULL;
}
else if (!strcmp(keyword, "ImageableArea"))
{
if ((size = ppdPageSize(ppd, name)) == NULL)
size = ppd_add_size(ppd, name);
if (size == NULL)
{
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);
_cupsStrFree(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 (!strcmp(keyword, "PageSize"))
{
if (ppdPageSize(ppd, name) == NULL)
ppd_add_size(ppd, name);
}
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);
choice->code = string;
string = NULL;
}
if (string &&
(mask & (PPD_KEYWORD | PPD_STRING)) == (PPD_KEYWORD | PPD_STRING))
ppd_add_attr(ppd, keyword, name, text, string);
else
_cupsStrFree(string);
}
if (group && pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_status = PPD_MISSING_CLOSE_GROUP;
goto error;
}
ppd_free(line.buffer);
#ifdef DEBUG
if (!cupsFileEOF(fp))
DEBUG_printf(("1_ppdOpen: Premature EOF at %lu...\n",
(unsigned long)cupsFileTell(fp)));
#endif
if (pg->ppd_status != PPD_OK)
{
ppdClose(ppd);
return (NULL);
}
if (!ppd_update_filters(ppd, pg))
{
ppdClose(ppd);
return (NULL);
}
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;
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;
}
}
ppd->marked = cupsArrayNew((cups_array_func_t)ppd_compare_choices, NULL);
return (ppd);
error:
_cupsStrFree(string);
ppd_free(line.buffer);
ppdClose(ppd);
return (NULL);
}
ppd_file_t *
ppdOpen(FILE *fp)
{
ppd_file_t *ppd;
cups_file_t *cf;
if ((cf = cupsFileOpenFd(fileno(fp), "r")) == NULL)
return (NULL);
ppd = _ppdOpen(cf, _PPD_LOCALIZATION_DEFAULT);
cupsFileClose(cf);
return (ppd);
}
ppd_file_t *
ppdOpen2(cups_file_t *fp)
{
return _ppdOpen(fp, _PPD_LOCALIZATION_DEFAULT);
}
ppd_file_t *
ppdOpenFd(int fd)
{
cups_file_t *fp;
ppd_file_t *ppd;
_ppd_globals_t *pg = _ppdGlobals();
pg->ppd_line = 0;
if (fd < 0)
{
pg->ppd_status = PPD_NULL_FILE;
return (NULL);
}
if ((fp = cupsFileOpenFd(fd, "r")) != NULL)
{
ppd = ppdOpen2(fp);
cupsFileClose(fp);
}
else
{
pg->ppd_status = PPD_FILE_OPEN_ERROR;
ppd = NULL;
}
return (ppd);
}
ppd_file_t *
_ppdOpenFile(const char *filename,
_ppd_localization_t localization)
{
cups_file_t *fp;
ppd_file_t *ppd;
_ppd_globals_t *pg = _ppdGlobals();
pg->ppd_line = 0;
if (filename == NULL)
{
pg->ppd_status = PPD_NULL_FILE;
return (NULL);
}
if ((fp = cupsFileOpen(filename, "r")) != NULL)
{
ppd = _ppdOpen(fp, localization);
cupsFileClose(fp);
}
else
{
pg->ppd_status = PPD_FILE_OPEN_ERROR;
ppd = NULL;
}
return (ppd);
}
ppd_file_t *
ppdOpenFile(const char *filename)
{
return _ppdOpenFile(filename, _PPD_LOCALIZATION_DEFAULT);
}
void
ppdSetConformance(ppd_conform_t c)
{
_ppd_globals_t *pg = _ppdGlobals();
pg->ppd_conform = c;
}
static ppd_attr_t *
ppd_add_attr(ppd_file_t *ppd,
const char *name,
const char *spec,
const char *text,
const char *value)
{
ppd_attr_t **ptr,
*temp;
if (ppd == NULL || name == NULL || spec == NULL)
return (NULL);
if (!ppd->sorted_attrs)
ppd->sorted_attrs = cupsArrayNew((cups_array_func_t)ppd_compare_attrs,
NULL);
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 ++;
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;
cupsArrayAdd(ppd->sorted_attrs, temp);
return (temp);
}
static ppd_choice_t *
ppd_add_choice(ppd_option_t *option,
const char *name)
{
ppd_choice_t *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);
}
static ppd_size_t *
ppd_add_size(ppd_file_t *ppd,
const char *name)
{
ppd_size_t *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);
}
static int
ppd_compare_attrs(ppd_attr_t *a,
ppd_attr_t *b)
{
return (_cups_strcasecmp(a->name, b->name));
}
static int
ppd_compare_choices(ppd_choice_t *a,
ppd_choice_t *b)
{
return (strcmp(a->option->keyword, b->option->keyword));
}
static int
ppd_compare_coptions(ppd_coption_t *a,
ppd_coption_t *b)
{
return (_cups_strcasecmp(a->keyword, b->keyword));
}
static int
ppd_compare_options(ppd_option_t *a,
ppd_option_t *b)
{
return (_cups_strcasecmp(a->keyword, b->keyword));
}
static int
ppd_decode(char *string)
{
char *inptr,
*outptr;
inptr = string;
outptr = string;
while (*inptr != '\0')
if (*inptr == '<' && isxdigit(inptr[1] & 255))
{
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));
}
static void
ppd_free_filters(ppd_file_t *ppd)
{
int i;
char **filter;
if (ppd->num_filters > 0)
{
for (i = ppd->num_filters, filter = ppd->filters; i > 0; i --, filter ++)
_cupsStrFree(*filter);
ppd_free(ppd->filters);
ppd->num_filters = 0;
ppd->filters = NULL;
}
}
static void
ppd_free_group(ppd_group_t *group)
{
int i;
ppd_option_t *option;
ppd_group_t *subgroup;
if (group->num_options > 0)
{
for (i = group->num_options, option = group->options;
i > 0;
i --, option ++)
ppd_free_option(option);
ppd_free(group->options);
}
if (group->num_subgroups > 0)
{
for (i = group->num_subgroups, subgroup = group->subgroups;
i > 0;
i --, subgroup ++)
ppd_free_group(subgroup);
ppd_free(group->subgroups);
}
}
static void
ppd_free_option(ppd_option_t *option)
{
int i;
ppd_choice_t *choice;
if (option->num_choices > 0)
{
for (i = option->num_choices, choice = option->choices;
i > 0;
i --, choice ++)
{
_cupsStrFree(choice->code);
}
ppd_free(option->choices);
}
}
static ppd_coption_t *
ppd_get_coption(ppd_file_t *ppd,
const char *name)
{
ppd_coption_t *copt;
if ((copt = ppdFindCustomOption(ppd, name)) != NULL)
return (copt);
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 (copt);
}
static ppd_cparam_t *
ppd_get_cparam(ppd_coption_t *opt,
const char *param,
const char *text)
{
ppd_cparam_t *cparam;
if ((cparam = ppdFindCustomParam(opt, param)) != NULL)
return (cparam);
if ((cparam = calloc(1, sizeof(ppd_cparam_t))) == NULL)
return (NULL);
strlcpy(cparam->name, param, sizeof(cparam->name));
strlcpy(cparam->text, text[0] ? text : param, sizeof(cparam->text));
cupsArrayAdd(opt->params, cparam);
return (cparam);
}
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)
{
int i;
ppd_group_t *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);
}
static ppd_option_t *
ppd_get_option(ppd_group_t *group,
const char *name)
{
int i;
ppd_option_t *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);
}
static _ppd_globals_t *
ppd_globals_alloc(void)
{
return ((_ppd_globals_t *)calloc(1, sizeof(_ppd_globals_t)));
}
#if defined(HAVE_PTHREAD_H) || defined(WIN32)
static void
ppd_globals_free(_ppd_globals_t *pg)
{
free(pg);
}
#endif
#ifdef HAVE_PTHREAD_H
static void
ppd_globals_init(void)
{
pthread_key_create(&ppd_globals_key, (void (*)(void *))ppd_globals_free);
}
#endif
static int
ppd_hash_option(ppd_option_t *option)
{
int hash = 0;
const char *k;
for (hash = option->keyword[0], k = option->keyword + 1; *k;)
hash = 33 * hash + *k++;
return (hash & 511);
}
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)
{
int ch,
col,
colon,
endquote,
mask,
startline,
textlen;
char *keyptr,
*optptr,
*textptr,
*strptr,
*lineptr;
*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
{
lineptr = line->buffer;
endquote = 0;
colon = 0;
while ((ch = cupsFileGetChar(fp)) != EOF)
{
if (lineptr >= (line->buffer + line->bufsize - 1))
{
char *temp;
line->bufsize += 1024;
if (line->bufsize > 262144)
{
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')
{
pg->ppd_line ++;
col = 0;
if (ch == '\r')
{
if ((ch = cupsFilePeekChar(fp)) == EOF)
{
ch = '\n';
break;
}
if (ch == 0x0a)
cupsFileGetChar(fp);
}
if (lineptr == line->buffer && ignoreblank)
continue;
ch = '\n';
if (!endquote)
break;
*lineptr++ = '\n';
}
else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_line = startline;
pg->ppd_status = PPD_ILLEGAL_CHARACTER;
return (0);
}
else if (ch != 0x1a)
{
*lineptr++ = (char)ch;
col ++;
if (col > (PPD_MAX_LINE - 1))
{
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)
{
while ((ch = cupsFileGetChar(fp)) != EOF)
if (ch == '\"')
break;
else if (ch == '\r' || ch == '\n')
{
pg->ppd_line ++;
col = 0;
if (ch == '\r')
{
if ((ch = cupsFilePeekChar(fp)) == EOF)
break;
if (ch == 0x0a)
cupsFileGetChar(fp);
}
}
else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_line = startline;
pg->ppd_status = PPD_ILLEGAL_CHARACTER;
return (0);
}
else if (ch != 0x1a)
{
col ++;
if (col > (PPD_MAX_LINE - 1))
{
pg->ppd_line = startline;
pg->ppd_status = PPD_LINE_TOO_LONG;
return (0);
}
}
}
if (ch != '\n')
{
while ((ch = cupsFileGetChar(fp)) != EOF)
if (ch == '\r' || ch == '\n')
{
pg->ppd_line ++;
col = 0;
if (ch == '\r')
{
if ((ch = cupsFilePeekChar(fp)) == EOF)
break;
if (ch == 0x0a)
cupsFileGetChar(fp);
}
break;
}
else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
{
pg->ppd_line = startline;
pg->ppd_status = PPD_ILLEGAL_CHARACTER;
return (0);
}
else if (ch != 0x1a)
{
col ++;
if (col > (PPD_MAX_LINE - 1))
{
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));
if (!strcmp(line->buffer, "*%APLWORKSET START"))
return (0);
if (ch == EOF && lineptr == line->buffer)
return (0);
mask = 0;
lineptr = line->buffer + 1;
keyword[0] = '\0';
option[0] = '\0';
text[0] = '\0';
*string = NULL;
if ((!line->buffer[0] ||
!strncmp(line->buffer, "*%", 2) ||
!strcmp(line->buffer, "*End")) &&
ignoreblank)
{
startline = pg->ppd_line + 1;
continue;
}
if (!strcmp(line->buffer, "*"))
{
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] != '*')
{
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);
}
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))
{
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 == '/')
{
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 == ':')
{
lineptr ++;
while (_cups_isspace(*lineptr))
lineptr ++;
strptr = lineptr + strlen(lineptr) - 1;
while (strptr >= lineptr && _cups_isspace(*strptr))
*strptr-- = '\0';
if (*strptr == '\"')
{
*strptr = '\0';
lineptr ++;
}
*string = _cupsStrAlloc(lineptr);
mask |= PPD_STRING;
}
}
while (mask == 0);
return (mask);
}
static int
ppd_update_filters(ppd_file_t *ppd,
_ppd_globals_t *pg)
{
ppd_attr_t *attr;
char srcsuper[16],
srctype[256],
dstsuper[16],
dsttype[256],
program[1024],
*ptr,
buffer[1024],
**filter;
int cost;
DEBUG_printf(("4ppd_update_filters(ppd=%p, cg=%p)", ppd, pg));
if ((attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) == NULL)
{
DEBUG_puts("5ppd_update_filters: No cupsFilter2 keywords present.");
return (1);
}
ppd_free_filters(ppd);
do
{
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));
}
snprintf(buffer, sizeof(buffer), "%s/%s %d %s", srcsuper, srctype, cost,
program);
DEBUG_printf(("5ppd_update_filters: Adding \"%s\".", buffer));
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 = _cupsStrAlloc(buffer);
}
while ((attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL);
DEBUG_puts("5ppd_update_filters: Completed OK.");
return (1);
}